xpine 0.0.6 → 0.0.7
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 +25 -5
- package/TODO +1 -2
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/types.d.ts +12 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/index.ts +6 -6
- package/package.json +9 -2
- package/tsconfig.json +13 -18
- package/types.d.ts +11 -0
- package/dist/src/build/dev-server.js +0 -10
- package/dist/src/build/spa.js +0 -152
package/README.md
CHANGED
|
@@ -14,9 +14,7 @@ export default {}
|
|
|
14
14
|
|
|
15
15
|
### Build
|
|
16
16
|
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
- Used for building production app
|
|
17
|
+
`xpine-build`
|
|
20
18
|
|
|
21
19
|
## Auth
|
|
22
20
|
|
|
@@ -24,7 +22,7 @@ export default {}
|
|
|
24
22
|
|
|
25
23
|
## Dev server
|
|
26
24
|
|
|
27
|
-
`
|
|
25
|
+
`xpine-dev`
|
|
28
26
|
|
|
29
27
|
## Config
|
|
30
28
|
|
|
@@ -36,4 +34,26 @@ export default {}
|
|
|
36
34
|
|
|
37
35
|
## Router
|
|
38
36
|
|
|
39
|
-
`import { createXPineRouter } from 'xpine';`
|
|
37
|
+
`import { createXPineRouter } from 'xpine';`
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### SPA interactivity
|
|
41
|
+
|
|
42
|
+
- data-spa="true"
|
|
43
|
+
- transforms link into client side URL update
|
|
44
|
+
- data-persistent="id"
|
|
45
|
+
- makes element persistent across pages that include the same persistent data-persistent tag
|
|
46
|
+
- data-spa-crossorigin="true"
|
|
47
|
+
- enables cross-origin spa navigation requests (untested)
|
|
48
|
+
|
|
49
|
+
#### Custom events
|
|
50
|
+
- spa:updatePageContent
|
|
51
|
+
- sent when the page content has update
|
|
52
|
+
- spa:updatePageURL
|
|
53
|
+
- send when the page URL has update
|
|
54
|
+
|
|
55
|
+
#### Custom scripts for certain pages
|
|
56
|
+
|
|
57
|
+
1. Add script to src/public/scripts/pages/your_script.ts
|
|
58
|
+
2. Import script into page HTML (e.g. <script src="/scripts/pages/your_script.ts">)
|
|
59
|
+
3. To unload event listeners, use `window.addEventListener('spa:updatePageURL', () => { remove event listeners here})` in the code
|
package/TODO
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
- npx create-xpine-app
|
|
2
|
-
- xpine-dev
|
|
1
|
+
- npx create-xpine-app
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './src/runDevServer.ts';
|
|
2
|
+
export * from './src/express.ts';
|
|
3
|
+
export * from './src/util/env.ts';
|
|
4
|
+
export * from './src/util/get-config.ts';
|
|
5
|
+
export * from './src/scripts/build.ts';
|
|
6
|
+
export * from './src/auth.ts';
|
|
7
|
+
export * from './src/util/html.ts';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
export type XPineConfig = {
|
|
3
|
+
[key: string]: string;
|
|
4
|
+
};
|
|
5
|
+
export type TokenUser = {
|
|
6
|
+
email?: string;
|
|
7
|
+
username?: string;
|
|
8
|
+
};
|
|
9
|
+
export type ServerRequest = Request & {
|
|
10
|
+
user?: TokenUser;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAAA"}
|
package/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from './src/runDevServer';
|
|
2
|
-
export * from './src/express';
|
|
3
|
-
export * from './src/util/env';
|
|
4
|
-
export * from './src/util/get-config';
|
|
5
|
-
export * from './src/scripts/build';
|
|
1
|
+
export * from './src/runDevServer.ts';
|
|
2
|
+
export * from './src/express.ts';
|
|
3
|
+
export * from './src/util/env.ts';
|
|
4
|
+
export * from './src/util/get-config.ts';
|
|
5
|
+
export * from './src/scripts/build.ts';
|
|
6
6
|
export * from './src/auth.ts';
|
|
7
|
-
export * from './src/util/html';
|
|
7
|
+
export * from './src/util/html.ts';
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xpine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
+
"@aws-sdk/client-secrets-manager": "^3.758.0",
|
|
6
7
|
"@tailwindcss/postcss": "^4.0.8",
|
|
7
8
|
"builtin-modules": "^4.0.0",
|
|
8
9
|
"chokidar": "^4.0.3",
|
|
@@ -19,13 +20,19 @@
|
|
|
19
20
|
"tsx": "^4.19.3",
|
|
20
21
|
"typescript": "^5.7.3"
|
|
21
22
|
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git://github.com/dan-caldwell/xpine.git"
|
|
26
|
+
},
|
|
22
27
|
"scripts": {
|
|
23
|
-
"build": "tsx ./src/scripts/build-module.ts"
|
|
28
|
+
"build": "tsx ./src/scripts/build-module.ts && npm run build:types",
|
|
29
|
+
"build:types": "npx tsc --declaration index.ts --declarationDir dist/lib --emitDeclarationOnly --declarationMap --skipLibCheck --esModuleInterop --allowImportingTsExtensions --target esnext --moduleResolution NodeNext --module NodeNext"
|
|
24
30
|
},
|
|
25
31
|
"bin": {
|
|
26
32
|
"xpine-build": "dist/src/scripts/xpine-build.js",
|
|
27
33
|
"xpine-dev": "dist/src/scripts/xpine-dev.js"
|
|
28
34
|
},
|
|
35
|
+
"types": "dist/lib/index.d.ts",
|
|
29
36
|
"type": "module",
|
|
30
37
|
"devDependencies": {
|
|
31
38
|
"@types/express": "^5.0.0",
|
package/tsconfig.json
CHANGED
|
@@ -21,29 +21,24 @@
|
|
|
21
21
|
"isolatedModules": true,
|
|
22
22
|
"jsxImportSource": "xpine",
|
|
23
23
|
"incremental": true,
|
|
24
|
-
"allowImportingTsExtensions": true
|
|
24
|
+
"allowImportingTsExtensions": true,
|
|
25
|
+
// Generate d.ts files
|
|
26
|
+
"declaration": true,
|
|
27
|
+
"emitDeclarationOnly": true,
|
|
28
|
+
"outDir": "dist",
|
|
29
|
+
"declarationMap": true,
|
|
30
|
+
"types": []
|
|
25
31
|
},
|
|
26
32
|
"include": [
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"postcss.config.mjs",
|
|
30
|
-
"tailwind.config.js",
|
|
31
|
-
"eslint.config.mjs",
|
|
32
|
-
"index.ts",
|
|
33
|
-
"src/runDevServer.ts",
|
|
34
|
-
"src/express.ts",
|
|
35
|
-
"src/scripts/build.ts",
|
|
36
|
-
"src/auth.ts",
|
|
37
|
-
"src/util/require.ts",
|
|
38
|
-
"src/util/get-config.ts",
|
|
39
|
-
"src/util/env.ts",
|
|
40
|
-
"src/scripts/xpine-dev.ts",
|
|
41
|
-
"src/scripts/xpine-build.ts",
|
|
42
|
-
"src/build/typescript-builder.ts"
|
|
33
|
+
"src/**/*.ts",
|
|
34
|
+
"index.ts"
|
|
43
35
|
],
|
|
44
36
|
"exclude": [
|
|
45
37
|
"node_modules",
|
|
46
|
-
"
|
|
38
|
+
"./node_modules",
|
|
39
|
+
"./node_modules/*",
|
|
40
|
+
"./node_modules/@types/node/index.d.ts",
|
|
41
|
+
"dist",
|
|
47
42
|
"**/*.js"
|
|
48
43
|
],
|
|
49
44
|
}
|
package/types.d.ts
ADDED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// src/build/dev-server.mjs
|
|
2
|
-
var socketProtocol = "ws:";
|
|
3
|
-
var echoSocketUrl = socketProtocol + "//" + window.location.hostname + ":3001/dev-server/";
|
|
4
|
-
var socket = new WebSocket(echoSocketUrl);
|
|
5
|
-
socket.addEventListener("message", async (msg) => {
|
|
6
|
-
if (msg.data === "refresh:client") {
|
|
7
|
-
window.location.reload();
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2J1aWxkL2Rldi1zZXJ2ZXIubWpzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBzb2NrZXRQcm90b2NvbCA9ICd3czonO1xuY29uc3QgZWNob1NvY2tldFVybCA9IHNvY2tldFByb3RvY29sICsgJy8vJyArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICc6MzAwMS9kZXYtc2VydmVyLyc7XG5jb25zdCBzb2NrZXQgPSBuZXcgV2ViU29ja2V0KGVjaG9Tb2NrZXRVcmwpO1xuXG5zb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGFzeW5jIChtc2cpID0+IHtcbiAgaWYgKG1zZy5kYXRhID09PSAncmVmcmVzaDpjbGllbnQnKSB7XG4gICAgd2luZG93LmxvY2F0aW9uLnJlbG9hZCgpO1xuICB9XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBQSxJQUFNLGlCQUFpQjtBQUN2QixJQUFNLGdCQUFnQixpQkFBaUIsT0FBTyxPQUFPLFNBQVMsV0FBVztBQUN6RSxJQUFNLFNBQVMsSUFBSSxVQUFVLGFBQWE7QUFFMUMsT0FBTyxpQkFBaUIsV0FBVyxPQUFPLFFBQVE7QUFDaEQsTUFBSSxJQUFJLFNBQVMsa0JBQWtCO0FBQ2pDLFdBQU8sU0FBUyxPQUFPO0FBQUEsRUFDekI7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
|
package/dist/src/build/spa.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// src/build/spa.mjs
|
|
2
|
-
async function replaceDocumentContentsWithLinkResponse() {
|
|
3
|
-
const links = [...document.querySelectorAll("[data-spa]")];
|
|
4
|
-
for (const link of links) {
|
|
5
|
-
link.addEventListener("click", updatePageOnLinkClick);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
async function updatePageOnLinkClick(e) {
|
|
9
|
-
const targetHref = e?.target?.getAttribute("href");
|
|
10
|
-
if (!targetHref) return;
|
|
11
|
-
if (isExternalURL(targetHref) && !e?.target?.getAttribute("data-spa-crossorigin")) return;
|
|
12
|
-
e.preventDefault();
|
|
13
|
-
try {
|
|
14
|
-
await getNewPageContent(targetHref);
|
|
15
|
-
if (targetHref === window.location.pathname) return;
|
|
16
|
-
window.history.pushState(
|
|
17
|
-
{
|
|
18
|
-
type: "link-click",
|
|
19
|
-
targetHref
|
|
20
|
-
},
|
|
21
|
-
"",
|
|
22
|
-
targetHref
|
|
23
|
-
);
|
|
24
|
-
const event = new CustomEvent("spa:updatePageURL", {
|
|
25
|
-
detail: {
|
|
26
|
-
href: targetHref
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
window.dispatchEvent(event);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
console.error(err);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
async function getNewPageContent(href) {
|
|
35
|
-
try {
|
|
36
|
-
const res = await fetch(href);
|
|
37
|
-
const text = await res.text();
|
|
38
|
-
const parser = new DOMParser();
|
|
39
|
-
const dom = parser.parseFromString(text, "text/html");
|
|
40
|
-
diffHead(dom);
|
|
41
|
-
const newScripts = removeBodyScripts(dom);
|
|
42
|
-
const clonedPersistentNodes = getPersistentNodesFromDocument(dom);
|
|
43
|
-
const body = dom.body.innerHTML;
|
|
44
|
-
document.body.innerHTML = body;
|
|
45
|
-
for (const clonedNode of clonedPersistentNodes) {
|
|
46
|
-
const newNode = document.body.querySelector(`[data-persistent="${clonedNode?.getAttribute("data-persistent")}"]`);
|
|
47
|
-
newNode.replaceWith(clonedNode);
|
|
48
|
-
}
|
|
49
|
-
replaceAttributesOnDocumentBody(dom);
|
|
50
|
-
replaceDocumentContentsWithLinkResponse();
|
|
51
|
-
for (const script of newScripts) document.body.appendChild(script);
|
|
52
|
-
const event = new CustomEvent("spa:updatePageContent", {
|
|
53
|
-
detail: {
|
|
54
|
-
href
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
window.dispatchEvent(event);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
console.error(err);
|
|
60
|
-
console.error("Error getting link", href);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function diffHead(dom) {
|
|
64
|
-
const domHeadChildren = [...dom.head.children];
|
|
65
|
-
const currentHeadChildren = [...document.head.children];
|
|
66
|
-
const result = {
|
|
67
|
-
childrenToAdd: [],
|
|
68
|
-
childrenToRemove: []
|
|
69
|
-
};
|
|
70
|
-
for (const domChild of domHeadChildren) {
|
|
71
|
-
const foundInCurrentChildren = currentHeadChildren.find((child) => child.outerHTML === domChild.outerHTML);
|
|
72
|
-
if (!foundInCurrentChildren) result.childrenToAdd.push(domChild);
|
|
73
|
-
}
|
|
74
|
-
for (const currentChild of currentHeadChildren) {
|
|
75
|
-
const foundInDomChildren = domHeadChildren.find((child) => child.outerHTML === currentChild.outerHTML);
|
|
76
|
-
if (!foundInDomChildren) result.childrenToRemove.push(currentChild);
|
|
77
|
-
}
|
|
78
|
-
applyHeadDiffsToDocument(result);
|
|
79
|
-
}
|
|
80
|
-
function applyHeadDiffsToDocument(diff) {
|
|
81
|
-
for (const child of diff.childrenToRemove) {
|
|
82
|
-
document.head.removeChild(child);
|
|
83
|
-
}
|
|
84
|
-
for (const child of diff.childrenToAdd) {
|
|
85
|
-
if (child?.nodeName?.toLowerCase() == "script") {
|
|
86
|
-
const scriptElement = document.createElement("script");
|
|
87
|
-
portAttributesToElement(child, scriptElement);
|
|
88
|
-
scriptElement.innerHTML = child.innerHTML;
|
|
89
|
-
document.head.appendChild(scriptElement);
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
document.head.appendChild(child);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function removeBodyScripts(dom) {
|
|
96
|
-
const scripts = [...dom.body.querySelectorAll("script")];
|
|
97
|
-
const newElements = [];
|
|
98
|
-
for (const script of scripts) {
|
|
99
|
-
const element = document.createElement("script");
|
|
100
|
-
portAttributesToElement(script, element);
|
|
101
|
-
element.innerHTML = script.innerHTML;
|
|
102
|
-
dom.body.removeChild(script);
|
|
103
|
-
newElements.push(element);
|
|
104
|
-
}
|
|
105
|
-
return newElements;
|
|
106
|
-
}
|
|
107
|
-
function portAttributesToElement(oldEl, newEl) {
|
|
108
|
-
for (const attribute of oldEl.attributes) {
|
|
109
|
-
newEl.setAttribute(attribute.name, attribute.nodeValue);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function getPersistentNodesFromDocument(dom) {
|
|
113
|
-
const persistentNodesFromDom = [...dom.querySelectorAll("[data-persistent]")].map((el) => el.getAttribute("data-persistent"));
|
|
114
|
-
const persistentNodesFromBody = [...document.body.querySelectorAll("[data-persistent]")];
|
|
115
|
-
const validPersistentNodes = persistentNodesFromBody.filter((el) => {
|
|
116
|
-
return persistentNodesFromDom.includes(el.getAttribute("data-persistent"));
|
|
117
|
-
});
|
|
118
|
-
return validPersistentNodes.map((el) => el.cloneNode(true));
|
|
119
|
-
}
|
|
120
|
-
function replaceAttributesOnDocumentBody(dom) {
|
|
121
|
-
while (document.body.attributes.length > 0) {
|
|
122
|
-
document.body.removeAttribute(document.body.attributes[0].name);
|
|
123
|
-
}
|
|
124
|
-
for (const attribute of dom.body.attributes) {
|
|
125
|
-
document.body.setAttribute(attribute.name, attribute.nodeValue);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function handleBackButton() {
|
|
129
|
-
await getNewPageContent(window.history.state?.targetHref || window.location.href);
|
|
130
|
-
}
|
|
131
|
-
function isRelativeURL(url) {
|
|
132
|
-
if (typeof url !== "string") return false;
|
|
133
|
-
try {
|
|
134
|
-
new URL(url);
|
|
135
|
-
return false;
|
|
136
|
-
} catch (err) {
|
|
137
|
-
if (url?.startsWith("//")) return false;
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function isExternalURL(url) {
|
|
142
|
-
if (isRelativeURL(url)) return false;
|
|
143
|
-
try {
|
|
144
|
-
const computedURL = new URL(url);
|
|
145
|
-
return computedURL?.origin !== window.location?.origin;
|
|
146
|
-
} catch {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
window.addEventListener("DOMContentLoaded", replaceDocumentContentsWithLinkResponse);
|
|
151
|
-
window.addEventListener("popstate", handleBackButton);
|
|
152
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2J1aWxkL3NwYS5tanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8vIEVuYWJsZXMgU1BBIGxpa2UgYmVoYXZpb3IgZm9yIGxpbmtzXG5cbmFzeW5jIGZ1bmN0aW9uIHJlcGxhY2VEb2N1bWVudENvbnRlbnRzV2l0aExpbmtSZXNwb25zZSgpIHtcbiAgY29uc3QgbGlua3MgPSBbLi4uZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnW2RhdGEtc3BhXScpXTtcbiAgZm9yIChjb25zdCBsaW5rIG9mIGxpbmtzKSB7XG4gICAgbGluay5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHVwZGF0ZVBhZ2VPbkxpbmtDbGljayk7XG4gIH1cblxufVxuXG5hc3luYyBmdW5jdGlvbiB1cGRhdGVQYWdlT25MaW5rQ2xpY2soZSkge1xuICBjb25zdCB0YXJnZXRIcmVmID0gZT8udGFyZ2V0Py5nZXRBdHRyaWJ1dGUoJ2hyZWYnKTtcbiAgaWYgKCF0YXJnZXRIcmVmKSByZXR1cm47XG4gIGlmIChpc0V4dGVybmFsVVJMKHRhcmdldEhyZWYpICYmICFlPy50YXJnZXQ/LmdldEF0dHJpYnV0ZSgnZGF0YS1zcGEtY3Jvc3NvcmlnaW4nKSkgcmV0dXJuO1xuICBlLnByZXZlbnREZWZhdWx0KCk7XG4gIHRyeSB7XG4gICAgYXdhaXQgZ2V0TmV3UGFnZUNvbnRlbnQodGFyZ2V0SHJlZik7XG4gICAgaWYgKHRhcmdldEhyZWYgPT09IHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSkgcmV0dXJuO1xuICAgIHdpbmRvdy5oaXN0b3J5LnB1c2hTdGF0ZShcbiAgICAgIHtcbiAgICAgICAgdHlwZTogJ2xpbmstY2xpY2snLFxuICAgICAgICB0YXJnZXRIcmVmLFxuICAgICAgfSxcbiAgICAgICcnLFxuICAgICAgdGFyZ2V0SHJlZlxuICAgICk7XG4gICAgY29uc3QgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3NwYTp1cGRhdGVQYWdlVVJMJywge1xuICAgICAgZGV0YWlsOiB7XG4gICAgICAgIGhyZWY6IHRhcmdldEhyZWYsXG4gICAgICB9LFxuICAgIH0pO1xuICAgIHdpbmRvdy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgY29uc29sZS5lcnJvcihlcnIpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldE5ld1BhZ2VDb250ZW50KGhyZWYpIHtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaChocmVmKTtcbiAgICBjb25zdCB0ZXh0ID0gYXdhaXQgcmVzLnRleHQoKTtcbiAgICBjb25zdCBwYXJzZXIgPSBuZXcgRE9NUGFyc2VyKCk7XG4gICAgY29uc3QgZG9tID0gcGFyc2VyLnBhcnNlRnJvbVN0cmluZyh0ZXh0LCAndGV4dC9odG1sJyk7XG4gICAgZGlmZkhlYWQoZG9tKTtcbiAgICBjb25zdCBuZXdTY3JpcHRzID0gcmVtb3ZlQm9keVNjcmlwdHMoZG9tKTtcbiAgICBjb25zdCBjbG9uZWRQZXJzaXN0ZW50Tm9kZXMgPSBnZXRQZXJzaXN0ZW50Tm9kZXNGcm9tRG9jdW1lbnQoZG9tKTtcbiAgICBjb25zdCBib2R5ID0gZG9tLmJvZHkuaW5uZXJIVE1MO1xuICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID0gYm9keTtcbiAgICBmb3IgKGNvbnN0IGNsb25lZE5vZGUgb2YgY2xvbmVkUGVyc2lzdGVudE5vZGVzKSB7XG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBjb25zdCBuZXdOb2RlID0gZG9jdW1lbnQuYm9keS5xdWVyeVNlbGVjdG9yKGBbZGF0YS1wZXJzaXN0ZW50PVwiJHtjbG9uZWROb2RlPy5nZXRBdHRyaWJ1dGUoJ2RhdGEtcGVyc2lzdGVudCcpfVwiXWApO1xuICAgICAgbmV3Tm9kZS5yZXBsYWNlV2l0aChjbG9uZWROb2RlKTtcbiAgICB9XG4gICAgcmVwbGFjZUF0dHJpYnV0ZXNPbkRvY3VtZW50Qm9keShkb20pO1xuICAgIHJlcGxhY2VEb2N1bWVudENvbnRlbnRzV2l0aExpbmtSZXNwb25zZSgpO1xuICAgIGZvciAoY29uc3Qgc2NyaXB0IG9mIG5ld1NjcmlwdHMpIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoc2NyaXB0KTtcbiAgICAvLyBTZW5kIGFuIGV2ZW50XG4gICAgY29uc3QgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3NwYTp1cGRhdGVQYWdlQ29udGVudCcsIHtcbiAgICAgIGRldGFpbDoge1xuICAgICAgICBocmVmLFxuICAgICAgfSxcbiAgICB9KTtcbiAgICB3aW5kb3cuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnNvbGUuZXJyb3IoZXJyKTtcbiAgICBjb25zb2xlLmVycm9yKCdFcnJvciBnZXR0aW5nIGxpbmsnLCBocmVmKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBkaWZmSGVhZChkb20pIHtcbiAgY29uc3QgZG9tSGVhZENoaWxkcmVuID0gWy4uLmRvbS5oZWFkLmNoaWxkcmVuXTtcbiAgY29uc3QgY3VycmVudEhlYWRDaGlsZHJlbiA9IFsuLi5kb2N1bWVudC5oZWFkLmNoaWxkcmVuXTtcbiAgY29uc3QgcmVzdWx0ID0ge1xuICAgIGNoaWxkcmVuVG9BZGQ6IFtdLFxuICAgIGNoaWxkcmVuVG9SZW1vdmU6IFtdLFxuICB9O1xuICBmb3IgKGNvbnN0IGRvbUNoaWxkIG9mIGRvbUhlYWRDaGlsZHJlbikge1xuICAgIGNvbnN0IGZvdW5kSW5DdXJyZW50Q2hpbGRyZW4gPSBjdXJyZW50SGVhZENoaWxkcmVuLmZpbmQoY2hpbGQgPT4gY2hpbGQub3V0ZXJIVE1MID09PSBkb21DaGlsZC5vdXRlckhUTUwpO1xuICAgIGlmICghZm91bmRJbkN1cnJlbnRDaGlsZHJlbikgcmVzdWx0LmNoaWxkcmVuVG9BZGQucHVzaChkb21DaGlsZCk7XG4gIH1cbiAgZm9yIChjb25zdCBjdXJyZW50Q2hpbGQgb2YgY3VycmVudEhlYWRDaGlsZHJlbikge1xuICAgIGNvbnN0IGZvdW5kSW5Eb21DaGlsZHJlbiA9IGRvbUhlYWRDaGlsZHJlbi5maW5kKGNoaWxkID0+IGNoaWxkLm91dGVySFRNTCA9PT0gY3VycmVudENoaWxkLm91dGVySFRNTCk7XG4gICAgaWYgKCFmb3VuZEluRG9tQ2hpbGRyZW4pIHJlc3VsdC5jaGlsZHJlblRvUmVtb3ZlLnB1c2goY3VycmVudENoaWxkKTtcbiAgfVxuICBhcHBseUhlYWREaWZmc1RvRG9jdW1lbnQocmVzdWx0KTtcbn1cblxuZnVuY3Rpb24gYXBwbHlIZWFkRGlmZnNUb0RvY3VtZW50KGRpZmYpIHtcbiAgZm9yIChjb25zdCBjaGlsZCBvZiBkaWZmLmNoaWxkcmVuVG9SZW1vdmUpIHtcbiAgICAvLyBSZW1vdmUgZnJvbSBoZWFkXG4gICAgZG9jdW1lbnQuaGVhZC5yZW1vdmVDaGlsZChjaGlsZCk7XG4gIH1cbiAgZm9yIChjb25zdCBjaGlsZCBvZiBkaWZmLmNoaWxkcmVuVG9BZGQpIHtcbiAgICAvLyBIYW5kbGUgc2NyaXB0IHRhZ3NcbiAgICBpZiAoY2hpbGQ/Lm5vZGVOYW1lPy50b0xvd2VyQ2FzZSgpID09ICdzY3JpcHQnKSB7XG4gICAgICBjb25zdCBzY3JpcHRFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7XG4gICAgICBwb3J0QXR0cmlidXRlc1RvRWxlbWVudChjaGlsZCwgc2NyaXB0RWxlbWVudCk7XG4gICAgICBzY3JpcHRFbGVtZW50LmlubmVySFRNTCA9IGNoaWxkLmlubmVySFRNTDtcbiAgICAgIGRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoc2NyaXB0RWxlbWVudCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgLy8gQWRkIHRvIHRoZSBoZWFkXG4gICAgZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChjaGlsZCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVtb3ZlQm9keVNjcmlwdHMoZG9tKSB7XG4gIGNvbnN0IHNjcmlwdHMgPSBbLi4uZG9tLmJvZHkucXVlcnlTZWxlY3RvckFsbCgnc2NyaXB0JyldO1xuICBjb25zdCBuZXdFbGVtZW50cyA9IFtdO1xuICBmb3IgKGNvbnN0IHNjcmlwdCBvZiBzY3JpcHRzKSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpO1xuICAgIHBvcnRBdHRyaWJ1dGVzVG9FbGVtZW50KHNjcmlwdCwgZWxlbWVudCk7XG4gICAgZWxlbWVudC5pbm5lckhUTUwgPSBzY3JpcHQuaW5uZXJIVE1MO1xuICAgIGRvbS5ib2R5LnJlbW92ZUNoaWxkKHNjcmlwdCk7XG4gICAgbmV3RWxlbWVudHMucHVzaChlbGVtZW50KTtcbiAgfVxuICByZXR1cm4gbmV3RWxlbWVudHM7XG59XG5cbmZ1bmN0aW9uIHBvcnRBdHRyaWJ1dGVzVG9FbGVtZW50KG9sZEVsLCBuZXdFbCkge1xuICBmb3IgKGNvbnN0IGF0dHJpYnV0ZSBvZiBvbGRFbC5hdHRyaWJ1dGVzKSB7XG4gICAgbmV3RWwuc2V0QXR0cmlidXRlKGF0dHJpYnV0ZS5uYW1lLCBhdHRyaWJ1dGUubm9kZVZhbHVlKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRQZXJzaXN0ZW50Tm9kZXNGcm9tRG9jdW1lbnQoZG9tKSB7XG4gIC8vIEZpbmQgcGVyc2lzdGVudCBub2Rlc1xuICBjb25zdCBwZXJzaXN0ZW50Tm9kZXNGcm9tRG9tID0gWy4uLmRvbS5xdWVyeVNlbGVjdG9yQWxsKCdbZGF0YS1wZXJzaXN0ZW50XScpXS5tYXAoZWwgPT4gZWwuZ2V0QXR0cmlidXRlKCdkYXRhLXBlcnNpc3RlbnQnKSk7XG4gIGNvbnN0IHBlcnNpc3RlbnROb2Rlc0Zyb21Cb2R5ID0gWy4uLmRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3RvckFsbCgnW2RhdGEtcGVyc2lzdGVudF0nKV07XG4gIC8vIENyb3NzIG1hdGNoIHRoZSBwZXJzaXN0ZW50IG5vZGVzIGJldHdlZW4gY3VycmVudCBwYWdlIGFuZCBuZXcgcGFnZSB0byBmaW5kIHZhbGlkIG9uZXNcbiAgY29uc3QgdmFsaWRQZXJzaXN0ZW50Tm9kZXMgPSBwZXJzaXN0ZW50Tm9kZXNGcm9tQm9keS5maWx0ZXIoZWwgPT4ge1xuICAgIHJldHVybiBwZXJzaXN0ZW50Tm9kZXNGcm9tRG9tLmluY2x1ZGVzKGVsLmdldEF0dHJpYnV0ZSgnZGF0YS1wZXJzaXN0ZW50JykpO1xuICB9KTtcbiAgLy8gQ2xvbmVcbiAgcmV0dXJuIHZhbGlkUGVyc2lzdGVudE5vZGVzLm1hcChlbCA9PiBlbC5jbG9uZU5vZGUodHJ1ZSkpO1xufVxuXG5mdW5jdGlvbiByZXBsYWNlQXR0cmlidXRlc09uRG9jdW1lbnRCb2R5KGRvbSkge1xuICB3aGlsZSAoZG9jdW1lbnQuYm9keS5hdHRyaWJ1dGVzLmxlbmd0aCA+IDApIHtcbiAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUF0dHJpYnV0ZShkb2N1bWVudC5ib2R5LmF0dHJpYnV0ZXNbMF0ubmFtZSk7XG4gIH1cbiAgLy8gU2V0IGF0dHJpYnV0ZXMgb24gdGhlIGJvZHlcbiAgZm9yIChjb25zdCBhdHRyaWJ1dGUgb2YgZG9tLmJvZHkuYXR0cmlidXRlcykge1xuICAgIGRvY3VtZW50LmJvZHkuc2V0QXR0cmlidXRlKGF0dHJpYnV0ZS5uYW1lLCBhdHRyaWJ1dGUubm9kZVZhbHVlKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBoYW5kbGVCYWNrQnV0dG9uKCkge1xuICBhd2FpdCBnZXROZXdQYWdlQ29udGVudCh3aW5kb3cuaGlzdG9yeS5zdGF0ZT8udGFyZ2V0SHJlZiB8fCB3aW5kb3cubG9jYXRpb24uaHJlZik7XG59XG5cbmZ1bmN0aW9uIGlzUmVsYXRpdmVVUkwodXJsKSB7XG4gIGlmICh0eXBlb2YgdXJsICE9PSAnc3RyaW5nJykgcmV0dXJuIGZhbHNlO1xuICB0cnkge1xuICAgIG5ldyBVUkwodXJsKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmICh1cmw/LnN0YXJ0c1dpdGgoJy8vJykpIHJldHVybiBmYWxzZTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBpc0V4dGVybmFsVVJMKHVybCkge1xuICBpZiAoaXNSZWxhdGl2ZVVSTCh1cmwpKSByZXR1cm4gZmFsc2U7XG4gIHRyeSB7XG4gICAgY29uc3QgY29tcHV0ZWRVUkwgPSBuZXcgVVJMKHVybCk7XG4gICAgcmV0dXJuIGNvbXB1dGVkVVJMPy5vcmlnaW4gIT09IHdpbmRvdy5sb2NhdGlvbj8ub3JpZ2luO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7IC8vIE5vdCBhIFVSTFxuICB9XG59XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgcmVwbGFjZURvY3VtZW50Q29udGVudHNXaXRoTGlua1Jlc3BvbnNlKTtcbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdwb3BzdGF0ZScsIGhhbmRsZUJhY2tCdXR0b24pO1xuXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsZUFBZSwwQ0FBMEM7QUFDdkQsUUFBTSxRQUFRLENBQUMsR0FBRyxTQUFTLGlCQUFpQixZQUFZLENBQUM7QUFDekQsYUFBVyxRQUFRLE9BQU87QUFDeEIsU0FBSyxpQkFBaUIsU0FBUyxxQkFBcUI7QUFBQSxFQUN0RDtBQUVGO0FBRUEsZUFBZSxzQkFBc0IsR0FBRztBQUN0QyxRQUFNLGFBQWEsR0FBRyxRQUFRLGFBQWEsTUFBTTtBQUNqRCxNQUFJLENBQUMsV0FBWTtBQUNqQixNQUFJLGNBQWMsVUFBVSxLQUFLLENBQUMsR0FBRyxRQUFRLGFBQWEsc0JBQXNCLEVBQUc7QUFDbkYsSUFBRSxlQUFlO0FBQ2pCLE1BQUk7QUFDRixVQUFNLGtCQUFrQixVQUFVO0FBQ2xDLFFBQUksZUFBZSxPQUFPLFNBQVMsU0FBVTtBQUM3QyxXQUFPLFFBQVE7QUFBQSxNQUNiO0FBQUEsUUFDRSxNQUFNO0FBQUEsUUFDTjtBQUFBLE1BQ0Y7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLElBQ0Y7QUFDQSxVQUFNLFFBQVEsSUFBSSxZQUFZLHFCQUFxQjtBQUFBLE1BQ2pELFFBQVE7QUFBQSxRQUNOLE1BQU07QUFBQSxNQUNSO0FBQUEsSUFDRixD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
|