edge.libx.js 0.0.1
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/LICENSE +21 -0
- package/README.md +49 -0
- package/bin/cli.sh +7 -0
- package/bin/set-secrets.ts +70 -0
- package/bin/test.ts +18 -0
- package/build/Main.d.ts +1 -0
- package/build/Main.js +6 -0
- package/build/Main.js.map +1 -0
- package/build/helpers/EdgeNetwork.d.ts +13 -0
- package/build/helpers/EdgeNetwork.js +100 -0
- package/build/helpers/EdgeNetwork.js.map +1 -0
- package/build/helpers/Exceptions.d.ts +37 -0
- package/build/helpers/Exceptions.js +100 -0
- package/build/helpers/Exceptions.js.map +1 -0
- package/build/helpers/getExpress.d.ts +4 -0
- package/build/helpers/getExpress.js +25 -0
- package/build/helpers/getExpress.js.map +1 -0
- package/build/helpers/index.d.ts +12 -0
- package/build/helpers/index.js +70 -0
- package/build/helpers/index.js.map +1 -0
- package/build/helpers/jwt.d.ts +57 -0
- package/build/helpers/jwt.js +227 -0
- package/build/helpers/jwt.js.map +1 -0
- package/build/helpers/localServer.d.ts +1 -0
- package/build/helpers/localServer.js +54 -0
- package/build/helpers/localServer.js.map +1 -0
- package/build/helpers/require.d.ts +1 -0
- package/build/helpers/require.js +219 -0
- package/build/helpers/require.js.map +1 -0
- package/build/modules/@module.d.ts +6 -0
- package/build/modules/@module.js +16 -0
- package/build/modules/@module.js.map +1 -0
- package/build/modules/RouterWrapper.d.ts +21 -0
- package/build/modules/RouterWrapper.js +62 -0
- package/build/modules/RouterWrapper.js.map +1 -0
- package/build/modules/cors.d.ts +15 -0
- package/build/modules/cors.js +117 -0
- package/build/modules/cors.js.map +1 -0
- package/jest.config.js +26 -0
- package/package.json +65 -0
- package/src/Main.ts +1 -0
- package/src/helpers/EdgeNetwork.ts +113 -0
- package/src/helpers/Exceptions.ts +102 -0
- package/src/helpers/getExpress.ts +25 -0
- package/src/helpers/index.ts +61 -0
- package/src/helpers/jwt.ts +287 -0
- package/src/helpers/localServer.ts +75 -0
- package/src/helpers/require.ts +331 -0
- package/src/modules/@module.ts +13 -0
- package/src/modules/RouterWrapper.ts +68 -0
- package/src/modules/cors.ts +153 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { libx } from 'libx.js/build/bundles/essentials.js';
|
|
2
|
+
import { Base64 } from 'js-base64'
|
|
3
|
+
import { network } from './EdgeNetwork.js';
|
|
4
|
+
|
|
5
|
+
class JwtHelper {
|
|
6
|
+
private static cachedFirebasePublicKeys = <any>{};
|
|
7
|
+
|
|
8
|
+
private static base64urlEncode(str): string {
|
|
9
|
+
return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private static objectToBase64url(payload) {
|
|
13
|
+
return this.arrayBufferToBase64Url(new TextEncoder().encode(JSON.stringify(payload)));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private static arrayBufferToBase64Url(buffer) {
|
|
17
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer)))
|
|
18
|
+
.replace(/=/g, '')
|
|
19
|
+
.replace(/\+/g, '-')
|
|
20
|
+
.replace(/\//g, '_');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private static hexToBuffer(hex: string) {
|
|
24
|
+
const matches = hex.match(/[\da-f]{2}/gi) ?? []; // grab hex pairs
|
|
25
|
+
const { buffer } = new Uint8Array(matches.map((h) => parseInt(h, 16)));
|
|
26
|
+
return buffer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static base64UrlDecode = (str) => {
|
|
30
|
+
return libx.Buffer.from(str, 'base64').toString('utf-8');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
private static async getGooglePublicKey(kid) {
|
|
34
|
+
if (this.cachedFirebasePublicKeys[kid]) return this.cachedFirebasePublicKeys[kid];
|
|
35
|
+
const result: any = await (
|
|
36
|
+
await fetch(
|
|
37
|
+
"https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"
|
|
38
|
+
// 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'
|
|
39
|
+
)
|
|
40
|
+
).json();
|
|
41
|
+
|
|
42
|
+
const res = result.keys.find((key) => key.kid === kid);
|
|
43
|
+
this.cachedFirebasePublicKeys[kid] = res;
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public static async generateSignedJWT(serviceAccount: any, scope: string, options?: Partial<Options>) {
|
|
48
|
+
options = { ...new Options(), ...options };
|
|
49
|
+
const pem = serviceAccount.private_key.replace(/\n/g, '');
|
|
50
|
+
|
|
51
|
+
const pemHeader = '-----BEGIN PRIVATE KEY-----';
|
|
52
|
+
const pemFooter = '-----END PRIVATE KEY-----';
|
|
53
|
+
|
|
54
|
+
if (!pem.startsWith(pemHeader) || !pem.endsWith(pemFooter)) {
|
|
55
|
+
throw new Error('Invalid service account private key');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
|
|
59
|
+
const buffer = Base64.toUint8Array(pemContents);
|
|
60
|
+
const algorithm = {
|
|
61
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
62
|
+
hash: {
|
|
63
|
+
name: 'SHA-256',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const extractable = false;
|
|
67
|
+
const privateKey = await crypto.subtle.importKey('pkcs8', buffer, algorithm, extractable, ["sign"]);
|
|
68
|
+
|
|
69
|
+
const header = Base64.encodeURI(
|
|
70
|
+
JSON.stringify({
|
|
71
|
+
alg: 'RS256',
|
|
72
|
+
typ: 'JWT',
|
|
73
|
+
kid: serviceAccount.private_key_id,
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
78
|
+
const exp = iat + options.exp;
|
|
79
|
+
const payload = Base64.encodeURI(
|
|
80
|
+
JSON.stringify({
|
|
81
|
+
iss: serviceAccount.client_email,
|
|
82
|
+
sub: serviceAccount.client_email,
|
|
83
|
+
scope,
|
|
84
|
+
aud: options.aud,
|
|
85
|
+
exp,
|
|
86
|
+
iat,
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const textEncoder = new TextEncoder();
|
|
91
|
+
const inputArrayBuffer = textEncoder.encode(`${header}.${payload}`);
|
|
92
|
+
const outputArrayBuffer = await crypto.subtle.sign({ name: 'RSASSA-PKCS1-v1_5' }, privateKey, inputArrayBuffer);
|
|
93
|
+
const signature = Base64.fromUint8Array(new Uint8Array(outputArrayBuffer), true);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
header,
|
|
97
|
+
payload,
|
|
98
|
+
signature
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public static async generateToken(serviceAccount: any, scope: string, options?: Partial<Options>) {
|
|
103
|
+
const signed = await this.generateSignedJWT(serviceAccount, scope, options);
|
|
104
|
+
const token = `${signed.header}.${signed.payload}.${signed.signature}`;
|
|
105
|
+
return token;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public static async generateOAuth(serviceAccount: any, scope: string, options?: Partial<Options>) {
|
|
109
|
+
const token = await this.generateToken(serviceAccount, scope, options);
|
|
110
|
+
return await this.jwtToOAuth(token);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private static async jwtToOAuth(signedJwt: string) {
|
|
114
|
+
// Prepare the request to get an OAuth2 token
|
|
115
|
+
const tokenRequest = {
|
|
116
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
117
|
+
assertion: signedJwt
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
/*
|
|
122
|
+
// Make the request to Google's OAuth2 server
|
|
123
|
+
const response = await axios.post('https://oauth2.googleapis.com/token', new URLSearchParams(tokenRequest).toString(), {
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Extract and return the access token
|
|
130
|
+
return response.data.access_token;
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
// Make the request to Google's OAuth2 server
|
|
134
|
+
const response = await network.httpPostJson('https://oauth2.googleapis.com/token', tokenRequest);
|
|
135
|
+
// const response = await fetch('https://www.googleapis.com/oauth2/v4/token', {
|
|
136
|
+
// method: 'POST',
|
|
137
|
+
// headers: new Headers({
|
|
138
|
+
// 'Content-Type': 'application/json',
|
|
139
|
+
// }),
|
|
140
|
+
// body: JSON.stringify(tokenRequest),
|
|
141
|
+
// });
|
|
142
|
+
|
|
143
|
+
// Extract and return the access token
|
|
144
|
+
const tmp = response as any;
|
|
145
|
+
// const tmp = await response.json<any>();
|
|
146
|
+
if (tmp.error) throw tmp;
|
|
147
|
+
return tmp.access_token;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Error fetching OAuth2 token:', error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public static async makeRequest(url: string, serviceAccountObj: Object, scope: string, payload: any, options = {
|
|
155
|
+
method: "POST",
|
|
156
|
+
exHeaders: {}
|
|
157
|
+
}) {
|
|
158
|
+
const token = await JwtHelper.generateOAuth(serviceAccountObj, scope);
|
|
159
|
+
|
|
160
|
+
libx.log.d('DBG: makeRequest: ', scope, token);
|
|
161
|
+
const bqResp = await fetch(url, {
|
|
162
|
+
method: options.method,
|
|
163
|
+
body: payload ? JSON.stringify(payload) : null,
|
|
164
|
+
headers: {
|
|
165
|
+
"content-type": "application/json;charset=UTF-8",
|
|
166
|
+
"Authorization": "Bearer " + token,
|
|
167
|
+
...options.exHeaders
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// libx.log.i('DONE!', JSON.stringify(await this.gatherResponse(bqResp)))
|
|
172
|
+
return bqResp;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public static async verifyFirebaseToken(token, expectAud): Promise<IFirebaseTokenPayload> {
|
|
176
|
+
const dur = libx.Measurement.start();
|
|
177
|
+
const expectIss = `https://securetoken.google.com/${expectAud}`;
|
|
178
|
+
const tokenParts = token.split('.');
|
|
179
|
+
const header = JSON.parse(atob(tokenParts[0]));
|
|
180
|
+
const key = await this.getGooglePublicKey(header.kid);
|
|
181
|
+
const payload = await this.verifyToken(key, token, expectAud, expectIss) as IFirebaseTokenPayload;
|
|
182
|
+
|
|
183
|
+
libx.log.d(`verifyFirebaseToken: dur: ${dur.peek()}ms`);
|
|
184
|
+
return payload;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public static async verifyToken(publicKey, token, expectAud, expectIss, checkExpiry = true): Promise<IJwtTokenPayload> {
|
|
188
|
+
try {
|
|
189
|
+
// const payload = this.decodeToken(token);
|
|
190
|
+
const payload2 = this.decodeToken(token);
|
|
191
|
+
|
|
192
|
+
if (expectAud && payload2.payload.aud != expectAud) {
|
|
193
|
+
throw 'verifyToken: mismatched aud!';
|
|
194
|
+
}
|
|
195
|
+
if (expectIss && payload2.payload.iss != expectIss) {
|
|
196
|
+
throw 'verifyToken: mismatched iss!';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const tokenParts = token.split('.');
|
|
200
|
+
const alg = { name: 'RSASSA-PKCS1-v1_5', hash: { name: 'SHA-256' } };
|
|
201
|
+
const key = await crypto.subtle.importKey('jwk', publicKey, alg, false, ['verify']);
|
|
202
|
+
const isVerified = await crypto.subtle.verify(alg, key, this.parseBase64Url(tokenParts[2]), this.utf8ToUint8Array(`${tokenParts[0]}.${tokenParts[1]}`));
|
|
203
|
+
|
|
204
|
+
if (checkExpiry && payload2.payload.exp <= Math.floor(Date.now() / 1000)) {
|
|
205
|
+
throw 'verifyToken: expired!';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isVerified == false) {
|
|
209
|
+
throw 'verifyToken: invalid token!';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return payload2.payload;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
libx.log.e('Error verifying token:', error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public static decodeToken(idToken) {
|
|
220
|
+
const [header, payload, signature] = idToken.split('.').map(part => this.base64UrlDecode(part));
|
|
221
|
+
return {
|
|
222
|
+
rawHeader: header,
|
|
223
|
+
header: JSON.parse(header),
|
|
224
|
+
payload: JSON.parse(payload),
|
|
225
|
+
rawPayload: payload,
|
|
226
|
+
signature: signature,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
private static parseBase64Url(url) {
|
|
231
|
+
return new Uint8Array(Array.prototype.map.call(atob(url.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')), c => c.charCodeAt(0)))
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private static utf8ToUint8Array(str) {
|
|
235
|
+
return this.parseBase64Url(btoa(unescape(encodeURIComponent(str))))
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private static decodeToken_2(token) {
|
|
239
|
+
let raw = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
240
|
+
switch (raw.length % 4) {
|
|
241
|
+
case 0:
|
|
242
|
+
break
|
|
243
|
+
case 2:
|
|
244
|
+
raw += '=='
|
|
245
|
+
break
|
|
246
|
+
case 3:
|
|
247
|
+
raw += '='
|
|
248
|
+
break
|
|
249
|
+
default:
|
|
250
|
+
throw new Error('Illegal base64url string!')
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
return JSON.parse(decodeURIComponent(escape(atob(raw))))
|
|
255
|
+
} catch {
|
|
256
|
+
return null
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
class Options {
|
|
262
|
+
aud = 'https://oauth2.googleapis.com/token';
|
|
263
|
+
exp = 3600;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
interface IJwtTokenPayload {
|
|
267
|
+
name: string;
|
|
268
|
+
picture: string;
|
|
269
|
+
iss: string;
|
|
270
|
+
aud: string;
|
|
271
|
+
auth_time: number,
|
|
272
|
+
user_id: string;
|
|
273
|
+
sub: string;
|
|
274
|
+
iat: number,
|
|
275
|
+
exp: number,
|
|
276
|
+
email: string;
|
|
277
|
+
email_verified: true,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface IFirebaseTokenPayload extends IJwtTokenPayload {
|
|
281
|
+
firebase: {
|
|
282
|
+
identities: any,
|
|
283
|
+
sign_in_provider: string;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export { JwtHelper };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createServerAdapter } from '@whatwg-node/server';
|
|
2
|
+
import 'isomorphic-fetch';
|
|
3
|
+
import { IRequest, Router, error, json } from 'itty-router';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import { getExpress } from './getExpress';
|
|
6
|
+
// import { router, server } from '../v5';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import { log, LogLevel } from 'libx.js/build/modules/log';
|
|
9
|
+
import { libx } from 'libx.js/build/bundles/node.essentials';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
12
|
+
log.isDebug = true;
|
|
13
|
+
log.filterLevel = LogLevel.All;
|
|
14
|
+
|
|
15
|
+
const envFile = '.env'; //'.dev.vars'
|
|
16
|
+
|
|
17
|
+
const entryPoints = libx.node.args._;
|
|
18
|
+
|
|
19
|
+
const { app } = getExpress();
|
|
20
|
+
|
|
21
|
+
// node-wrangler bridge:
|
|
22
|
+
// global.crypto = require('crypto');
|
|
23
|
+
// global.TransformStream = require('web-streams-polyfill').TransformStream;
|
|
24
|
+
|
|
25
|
+
function getEnvVars() {
|
|
26
|
+
const envFilePath = path.join(process.cwd(), envFile);
|
|
27
|
+
if (!fs.existsSync(envFilePath)) return null;
|
|
28
|
+
const content = fs.readFileSync(envFilePath)?.toString();
|
|
29
|
+
const ret = {};
|
|
30
|
+
for (let line of content.split('\n')) {
|
|
31
|
+
const parts = line.split('=');
|
|
32
|
+
if (parts[0].trim().startsWith('#') || parts[1] == null) continue;
|
|
33
|
+
ret[parts[0].trim()] = parts[1].replace(/\s*[\"\'](.+)[\"\']\s*/gi, '$1');
|
|
34
|
+
}
|
|
35
|
+
return ret;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
libx.node.catchErrors((err) => {
|
|
39
|
+
console.error('!!!!!! ERROR: ', err);
|
|
40
|
+
}, false);
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
// only lunch manual local server if using 'dev' command
|
|
45
|
+
if (['debug', 'debug:watch'].indexOf(process.env.npm_lifecycle_event) !== -1) {
|
|
46
|
+
const env = getEnvVars() ?? { a: 1 };
|
|
47
|
+
|
|
48
|
+
for (let entryPoint of entryPoints) {
|
|
49
|
+
const dir = process.cwd(); // process.argv[1]
|
|
50
|
+
const { handler, prefix } = require(`${dir}/${entryPoint}`);
|
|
51
|
+
app.use(
|
|
52
|
+
prefix ?? '/api',
|
|
53
|
+
createServerAdapter((request, env) =>
|
|
54
|
+
handler(<IRequest>request, env))
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// app.use(
|
|
59
|
+
// '/v5-n',
|
|
60
|
+
// createServerAdapter((request, env) =>
|
|
61
|
+
// handler_node(<IRequest>request, env))
|
|
62
|
+
// // handlerRedirect(<IRequest>request, env))
|
|
63
|
+
// );
|
|
64
|
+
|
|
65
|
+
const port = 8080;
|
|
66
|
+
try {
|
|
67
|
+
app.listen(port, () => {
|
|
68
|
+
console.log(`Server listening on http://0.0.0.0:${port}`);
|
|
69
|
+
});
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.warn(`LOCAL: Failed to start local server on port: ${port}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// export default server; //routes.fetch;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
const reqSetup = (function () {
|
|
4
|
+
// var require = libx.browser.require;
|
|
5
|
+
|
|
6
|
+
var mod = {};
|
|
7
|
+
|
|
8
|
+
(function (load) {
|
|
9
|
+
'use strict';
|
|
10
|
+
var RequireError = function (message, fileName, lineNumber) {
|
|
11
|
+
this.name = 'RequireError';
|
|
12
|
+
this.message = message;
|
|
13
|
+
};
|
|
14
|
+
RequireError.prototype = Object.create(Error.prototype);
|
|
15
|
+
|
|
16
|
+
// NOTE Mozilla still sets the wrong fileName porperty for errors that occur
|
|
17
|
+
// inside an eval call (even with sourceURL). However, the stack
|
|
18
|
+
// contains the correct source, so it can be used to re-threw the error
|
|
19
|
+
// with the correct fileName property.
|
|
20
|
+
// NOTE Re-threwing a new error object will mess up the stack trace and the
|
|
21
|
+
// column number.
|
|
22
|
+
if (typeof new Error().fileName == 'string') {
|
|
23
|
+
self.addEventListener(
|
|
24
|
+
'error',
|
|
25
|
+
function (evt) {
|
|
26
|
+
if (evt.error instanceof Error) {
|
|
27
|
+
if (pwd[0]) {
|
|
28
|
+
evt.preventDefault();
|
|
29
|
+
throw new evt.error.constructor(evt.error.message, pwd[0].uri, evt.error.lineNumber);
|
|
30
|
+
} else {
|
|
31
|
+
var m = evt.error.stack.match(/^[^\n@]*@([^\n]+):\d+:\d+/);
|
|
32
|
+
if (m === null) {
|
|
33
|
+
console.warn('libx.browser.require: unable to read file name from stack');
|
|
34
|
+
} else if (evt.error.fileName != m[1]) {
|
|
35
|
+
evt.preventDefault();
|
|
36
|
+
throw new evt.error.constructor(evt.error.message, m[1], evt.error.lineNumber);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
false
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// INFO Current module descriptors
|
|
46
|
+
// pwd[0] contains the descriptor of the currently loaded module,
|
|
47
|
+
// pwd[1] contains the descriptor its parent module and so on.
|
|
48
|
+
|
|
49
|
+
var pwd = Array();
|
|
50
|
+
|
|
51
|
+
// INFO Path parser
|
|
52
|
+
// NOTE Older browsers don't support the URL interface, therefore we use an
|
|
53
|
+
// anchor element as parser in that case. Thes breaks web worker support,
|
|
54
|
+
// but we don't care since these browser also don't support web workers.
|
|
55
|
+
|
|
56
|
+
var parser = URL ? new URL(location.href) : document.createElement('A');
|
|
57
|
+
|
|
58
|
+
// INFO Module cache
|
|
59
|
+
// NOTE Contains getter functions for the exports objects of all the loaded
|
|
60
|
+
// modules. The getter for the module 'mymod' is name '$name' to prevent
|
|
61
|
+
// collisions with predefined object properties (see note below).
|
|
62
|
+
// As long as a module has not been loaded the getter is either undefined
|
|
63
|
+
// or contains the module code as a function (in case the module has been
|
|
64
|
+
// pre-loaded in a bundle).
|
|
65
|
+
// NOTE IE8 supports defineProperty only for DOM objects, therfore we use a
|
|
66
|
+
// HTMLDivElement as cache in that case. This breaks web worker support,
|
|
67
|
+
// but we don't care since IE8 has no web workers at all.
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
var cache = new Object();
|
|
71
|
+
Object.defineProperty(cache, 'foo', { value: 'bar', configurable: true });
|
|
72
|
+
delete cache.foo;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.warn('Falling back to DOM workaround for defineProperty: ' + e);
|
|
75
|
+
cache = document.createElement('DIV');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// INFO Send lock
|
|
79
|
+
// NOTE Sending the request causes the event loop to continue. Therefore
|
|
80
|
+
// pending AJAX load events for the same url might be executed before
|
|
81
|
+
// the synchronous onLoad is called. This should be no problem, but in
|
|
82
|
+
// Chrome the responseText of the sneaked in load events will be empty.
|
|
83
|
+
// Therefore we have to lock the loading while executing send().
|
|
84
|
+
|
|
85
|
+
var lock = new Object();
|
|
86
|
+
|
|
87
|
+
var requirePath = mod && mod.requirePath !== undefined ? mod.requirePath.slice(0) : ['./'];
|
|
88
|
+
var requireCompiler = mod && mod.requireCompiler !== undefined ? mod.requireCompiler : null;
|
|
89
|
+
|
|
90
|
+
// NOTE Parse module root paths
|
|
91
|
+
var base = [location.origin, location.href.substr(0, location.href.lastIndexOf('/') + 1)];
|
|
92
|
+
for (var i = 0; i < requirePath.length; i++) {
|
|
93
|
+
parser.href = (requirePath[i][0] == '.' ? base[1] : base[0]) + requirePath[i];
|
|
94
|
+
requirePath[i] = parser.href;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// NOTE Add preloaded modules to cache
|
|
98
|
+
for (var id in mod && mod.requirePreloaded) cache['$' + resolve(id).id] = mod.requirePreloaded[id].toString();
|
|
99
|
+
|
|
100
|
+
// NOTE Add module overrides to cache
|
|
101
|
+
for (var id in mod && mod.requireOverrides) cache['$' + resolve(id).id] = mod.requireOverrides[id];
|
|
102
|
+
|
|
103
|
+
// INFO Module getter
|
|
104
|
+
// Takes a module identifier, resolves it and gets the module code via an
|
|
105
|
+
// AJAX request from the module URI. If this was successful the code and
|
|
106
|
+
// some environment variables are passed to the load function. The return
|
|
107
|
+
// value is the module's `exports` object. If the cache already
|
|
108
|
+
// contains an object for the module id, this object is returned directly.
|
|
109
|
+
// NOTE If a callback function has been passed, the AJAX request is asynchronous
|
|
110
|
+
// and the module exports are passed to the callback function after the
|
|
111
|
+
// module has been loaded.
|
|
112
|
+
|
|
113
|
+
mod = async function (identifier, callback, compiler) {
|
|
114
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
115
|
+
if (identifier instanceof Array) {
|
|
116
|
+
var modules = new Array();
|
|
117
|
+
var modcount = identifier.length;
|
|
118
|
+
for (var index = 0; index < identifier.length; index++) {
|
|
119
|
+
(function (id, i) {
|
|
120
|
+
modules.push(
|
|
121
|
+
mod(
|
|
122
|
+
id,
|
|
123
|
+
callback &&
|
|
124
|
+
function (mod) {
|
|
125
|
+
modules[i] = mod;
|
|
126
|
+
--modcount == 0 && callback(modules);
|
|
127
|
+
},
|
|
128
|
+
compiler
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
})(identifier[index], index);
|
|
132
|
+
}
|
|
133
|
+
return modules;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
compiler = compiler !== undefined ? compiler : requireCompiler;
|
|
137
|
+
var descriptor = resolve(identifier);
|
|
138
|
+
var cacheid = '$' + descriptor.id;
|
|
139
|
+
|
|
140
|
+
if (cache[cacheid]) {
|
|
141
|
+
if (typeof cache[cacheid] === 'string') load(descriptor, cache, pwd, cache[cacheid]);
|
|
142
|
+
// NOTE The callback should always be called asynchronously to ensure
|
|
143
|
+
// that a cached call won't differ from an uncached one.
|
|
144
|
+
callback &&
|
|
145
|
+
setTimeout(function () {
|
|
146
|
+
callback(cache[cacheid]);
|
|
147
|
+
}, 0);
|
|
148
|
+
return resolvePromise(cache[cacheid]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var request = new XMLHttpRequest();
|
|
152
|
+
|
|
153
|
+
// NOTE IE8 doesn't support the onload event, therefore we use
|
|
154
|
+
// onreadystatechange as a fallback here. However, onreadystatechange
|
|
155
|
+
// shouldn't be used for all browsers, since at least mobile Safari
|
|
156
|
+
// seems to have an issue where onreadystatechange is called twice for
|
|
157
|
+
// readyState 4.
|
|
158
|
+
callback && (request[request.onload === null ? 'onload' : 'onreadystatechange'] = onLoad);
|
|
159
|
+
request.addEventListener('load', onLoad);
|
|
160
|
+
request.open('GET', descriptor.uri, true); //!!callback);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// request.setRequestHeader('Origin', window.location.hostname);
|
|
164
|
+
// request.setRequestHeader('Access-Control-Allow-Origin', '*');
|
|
165
|
+
// request.setRequestHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS');
|
|
166
|
+
// request.setRequestHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token');
|
|
167
|
+
} catch (ex) {
|
|
168
|
+
console.warn('libx.require: Error setting CORS headers. ', ex);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
lock[cacheid] = lock[cacheid]++ || 1;
|
|
172
|
+
request.send();
|
|
173
|
+
!callback && onLoad();
|
|
174
|
+
|
|
175
|
+
function onLoad() {
|
|
176
|
+
try {
|
|
177
|
+
if (request.readyState != 4) return;
|
|
178
|
+
if (request.status != 200)
|
|
179
|
+
new RequireError('unable to load ' + descriptor.id + ' (' + request.status + ' ' + request.statusText + ')');
|
|
180
|
+
if (request.status == 200) lock[cacheid]--;
|
|
181
|
+
if (lock[cacheid]) {
|
|
182
|
+
console.warn('module locked: ' + descriptor.id);
|
|
183
|
+
if (lock[cacheid] <= 5) return;
|
|
184
|
+
callback && setTimeout(onLoad, 0);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!cache[cacheid]) {
|
|
188
|
+
var source = compiler ? compiler(request.responseText) : request.responseText;
|
|
189
|
+
load(descriptor, cache, pwd, source, typeof callback == 'boolean');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
!callback && onLoad();
|
|
193
|
+
|
|
194
|
+
callback && callback(cache[cacheid]);
|
|
195
|
+
resolvePromise(cache[cacheid]);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
rejectPromise(err);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// INFO Module resolver
|
|
204
|
+
// Takes a module identifier and resolves it to a module id and URI. Both
|
|
205
|
+
// values are returned as a module descriptor, which can be passed to
|
|
206
|
+
// `fetch` to load a module.
|
|
207
|
+
|
|
208
|
+
function fixSlash(url) {
|
|
209
|
+
var isDoubleSlash = url.startsWith('//');
|
|
210
|
+
var ret = url.replace(/[\/]+/g, '/').replace(':/', '://'); //.replace(/\/\//g, '/').replace(':/', '://');
|
|
211
|
+
if (isDoubleSlash) ret = ret.replace(/^\//, '//');
|
|
212
|
+
return ret;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function resolve(identifier) {
|
|
216
|
+
if (
|
|
217
|
+
identifier.indexOf('http://') == 0 ||
|
|
218
|
+
identifier.indexOf('https://') == 0 ||
|
|
219
|
+
identifier.indexOf('//') == 0 ||
|
|
220
|
+
identifier.indexOf('://') != -1
|
|
221
|
+
)
|
|
222
|
+
return { id: identifier, uri: fixSlash(identifier) };
|
|
223
|
+
|
|
224
|
+
if (identifier.indexOf('/') == 0) {
|
|
225
|
+
var root = requirePath[0].substr(0, requirePath[0].indexOf('/', requirePath[0].indexOf('://') + 3));
|
|
226
|
+
return { id: identifier, uri: fixSlash(root + '/' + identifier) };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { id: identifier, uri: fixSlash(identifier) };
|
|
230
|
+
|
|
231
|
+
// NOTE Matches [1]:[..]/[path/to/][file][.js]
|
|
232
|
+
var m = identifier.match(/^(?:([^:\/]+):)?(\.\.?)?\/?((?:.*\/)?)([^\.]+)?(\..*)?$/);
|
|
233
|
+
// NOTE Matches [1]:[/path/to/]file.js
|
|
234
|
+
var p = (pwd[0] ? pwd[0].id : '').match(/^(?:([^:\/]+):)?(.*\/|)[^\/]*$/);
|
|
235
|
+
var root = m[2] ? requirePath[p[1] ? parseInt(p[1]) : 0] : requirePath[m[1] ? parseInt(m[1]) : 0];
|
|
236
|
+
parser.href = (m[2] ? root + p[2] + m[2] + '/' : root) + m[3] + (m[4] ? m[4] : 'index');
|
|
237
|
+
var uri = parser.href + (m[5] ? m[5] : '.js');
|
|
238
|
+
if (uri.substr(0, root.length) != root) throw new RequireError('Relative identifier outside of module root');
|
|
239
|
+
var id = (m[1] ? m[1] + ':' : '0:') + parser.href.substr(root.length);
|
|
240
|
+
|
|
241
|
+
return { id: id, uri: fixSlash(uri) };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// INFO Exporting require to global scope
|
|
245
|
+
|
|
246
|
+
if (mod !== undefined) {
|
|
247
|
+
//throw new RequireError('\'require\' already defined in global scope');
|
|
248
|
+
//console.warn("'\'require\' already defined in global scope'");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
Object.defineProperty('require', { value: mod });
|
|
254
|
+
Object.defineProperty(mod, 'resolve', { value: resolve });
|
|
255
|
+
Object.defineProperty(mod, 'path', {
|
|
256
|
+
get: function () {
|
|
257
|
+
return requirePath.slice(0);
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
} catch (e) {
|
|
261
|
+
// NOTE IE8 can't use defineProperty on non-DOM objects, so we have to fall
|
|
262
|
+
// back to unsave property assignments in this case.
|
|
263
|
+
mod.resolve = resolve;
|
|
264
|
+
mod.path = requirePath.slice(0);
|
|
265
|
+
}
|
|
266
|
+
})(
|
|
267
|
+
// INFO Module loader
|
|
268
|
+
// Takes the module descriptor, the global variables and the module code,
|
|
269
|
+
// sets up the module envirinment, defines the module getter in the cache
|
|
270
|
+
// and evaluates the module code. If module is a bundle the code of the
|
|
271
|
+
// pre-loaded modules will be stored in the cache afterwards.
|
|
272
|
+
// NOTE This functions is defined as an anonymous function, which is passed as
|
|
273
|
+
// a parameter to the closure above to provide a clean environment (only
|
|
274
|
+
// global variables, module and exports) for the loaded module. This is
|
|
275
|
+
// also the reason why `source`, `pwd` & `cache` are not named parameters.
|
|
276
|
+
// NOTE If we would strict use mode here, the evaluated code would be forced to be
|
|
277
|
+
// in strict mode, too.
|
|
278
|
+
|
|
279
|
+
function (/*load*/ module /*, cache, pwd, source, isNotModule*/) {
|
|
280
|
+
var global = self;
|
|
281
|
+
var exports = new Object();
|
|
282
|
+
Object.defineProperty(module, 'exports', {
|
|
283
|
+
get: function () {
|
|
284
|
+
return exports;
|
|
285
|
+
},
|
|
286
|
+
set: function (e) {
|
|
287
|
+
exports = e;
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
arguments[2].unshift(module);
|
|
291
|
+
Object.defineProperty(arguments[1], '$' + module.id, {
|
|
292
|
+
get: function () {
|
|
293
|
+
return exports;
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
var content = arguments[3];
|
|
298
|
+
var isNotModule = arguments[4];
|
|
299
|
+
|
|
300
|
+
if (isNotModule === true) {
|
|
301
|
+
eval(content);
|
|
302
|
+
} else {
|
|
303
|
+
var extra = 'var __moduleUri = "' + module.uri + '";\n';
|
|
304
|
+
if (!module.uri.endsWith('.js')) {
|
|
305
|
+
content = 'module.exports = ' + content;
|
|
306
|
+
}
|
|
307
|
+
var script = 'function(){\n' + extra + content + '\n}';
|
|
308
|
+
let wrapper = '(' + script + ')();\n//# sourceURL=' + module.uri;
|
|
309
|
+
|
|
310
|
+
eval(wrapper);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// var script = '(function(__moduleUri){\n ('+arguments[3]+')}(); \n'
|
|
314
|
+
// arguments[3] = '(' + extra + script+')("' + module.uri + '");\nsourceURL="'+module.uri+'"';
|
|
315
|
+
|
|
316
|
+
// load(descriptor, cache, pwd, 'function(){\n'+source+'\n}');
|
|
317
|
+
// arguments[3] = '('+arguments[3]+')();\n//# sourceURL='+module.uri;
|
|
318
|
+
|
|
319
|
+
// if (typeof Function != "undefined") new Function(arguments[3]);
|
|
320
|
+
// else
|
|
321
|
+
// NOTE Store module code in the cache if the loaded file is a bundle
|
|
322
|
+
if (typeof module.id !== 'string') for (id in module) arguments[1]['$' + mod.resolve(id).id] = module[id].toString();
|
|
323
|
+
arguments[2].shift();
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
return mod;
|
|
328
|
+
})();
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
export const require = reqSetup();
|