- Add .gitignore: exclude compiled binaries, build artifacts, and Helm values files containing real secrets (authentik, prometheus) - Add all Kubernetes deployment manifests (deployment/) - Add services source code: ha-sync, device-inventory, games-console, paperclip, parts-inventory - Add Ansible orchestration: playbooks, roles, inventory, cloud-init - Add hardware specs, execution plans, scripts, HOMELAB.md - Add skills/homelab/SKILL.md + skills/install.sh to preserve Copilot skill - Remove previously-tracked inventory-cli binary from git index Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
233 lines
5 KiB
JavaScript
233 lines
5 KiB
JavaScript
"use strict";
|
|
const usm = require("./url-state-machine");
|
|
const urlencoded = require("./urlencoded");
|
|
const URLSearchParams = require("./URLSearchParams");
|
|
|
|
exports.implementation = class URLImpl {
|
|
// Unlike the spec, we duplicate some code between the constructor and canParse, because we want to give useful error
|
|
// messages in the constructor that distinguish between the different causes of failure.
|
|
constructor(globalObject, [url, base]) {
|
|
let parsedBase = null;
|
|
if (base !== undefined) {
|
|
parsedBase = usm.basicURLParse(base);
|
|
if (parsedBase === null) {
|
|
throw new TypeError(`Invalid base URL: ${base}`);
|
|
}
|
|
}
|
|
|
|
const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
|
|
if (parsedURL === null) {
|
|
throw new TypeError(`Invalid URL: ${url}`);
|
|
}
|
|
|
|
const query = parsedURL.query !== null ? parsedURL.query : "";
|
|
|
|
this._url = parsedURL;
|
|
|
|
// We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
|
|
// question mark by default. Therefore the doNotStripQMark hack is used.
|
|
this._query = URLSearchParams.createImpl(globalObject, [query], { doNotStripQMark: true });
|
|
this._query._url = this;
|
|
}
|
|
|
|
static parse(globalObject, input, base) {
|
|
try {
|
|
return new URLImpl(globalObject, [input, base]);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static canParse(url, base) {
|
|
let parsedBase = null;
|
|
if (base !== undefined) {
|
|
parsedBase = usm.basicURLParse(base);
|
|
if (parsedBase === null) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
|
|
if (parsedURL === null) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
get href() {
|
|
return usm.serializeURL(this._url);
|
|
}
|
|
|
|
set href(v) {
|
|
const parsedURL = usm.basicURLParse(v);
|
|
if (parsedURL === null) {
|
|
throw new TypeError(`Invalid URL: ${v}`);
|
|
}
|
|
|
|
this._url = parsedURL;
|
|
|
|
this._query._list.splice(0);
|
|
const { query } = parsedURL;
|
|
if (query !== null) {
|
|
this._query._list = urlencoded.parseUrlencodedString(query);
|
|
}
|
|
}
|
|
|
|
get origin() {
|
|
return usm.serializeURLOrigin(this._url);
|
|
}
|
|
|
|
get protocol() {
|
|
return `${this._url.scheme}:`;
|
|
}
|
|
|
|
set protocol(v) {
|
|
usm.basicURLParse(`${v}:`, { url: this._url, stateOverride: "scheme start" });
|
|
}
|
|
|
|
get username() {
|
|
return this._url.username;
|
|
}
|
|
|
|
set username(v) {
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
return;
|
|
}
|
|
|
|
usm.setTheUsername(this._url, v);
|
|
}
|
|
|
|
get password() {
|
|
return this._url.password;
|
|
}
|
|
|
|
set password(v) {
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
return;
|
|
}
|
|
|
|
usm.setThePassword(this._url, v);
|
|
}
|
|
|
|
get host() {
|
|
const url = this._url;
|
|
|
|
if (url.host === null) {
|
|
return "";
|
|
}
|
|
|
|
if (url.port === null) {
|
|
return usm.serializeHost(url.host);
|
|
}
|
|
|
|
return `${usm.serializeHost(url.host)}:${usm.serializeInteger(url.port)}`;
|
|
}
|
|
|
|
set host(v) {
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
return;
|
|
}
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "host" });
|
|
}
|
|
|
|
get hostname() {
|
|
if (this._url.host === null) {
|
|
return "";
|
|
}
|
|
|
|
return usm.serializeHost(this._url.host);
|
|
}
|
|
|
|
set hostname(v) {
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
return;
|
|
}
|
|
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" });
|
|
}
|
|
|
|
get port() {
|
|
if (this._url.port === null) {
|
|
return "";
|
|
}
|
|
|
|
return usm.serializeInteger(this._url.port);
|
|
}
|
|
|
|
set port(v) {
|
|
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
|
return;
|
|
}
|
|
|
|
if (v === "") {
|
|
this._url.port = null;
|
|
} else {
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "port" });
|
|
}
|
|
}
|
|
|
|
get pathname() {
|
|
return usm.serializePath(this._url);
|
|
}
|
|
|
|
set pathname(v) {
|
|
if (usm.hasAnOpaquePath(this._url)) {
|
|
return;
|
|
}
|
|
|
|
this._url.path = [];
|
|
usm.basicURLParse(v, { url: this._url, stateOverride: "path start" });
|
|
}
|
|
|
|
get search() {
|
|
if (this._url.query === null || this._url.query === "") {
|
|
return "";
|
|
}
|
|
|
|
return `?${this._url.query}`;
|
|
}
|
|
|
|
set search(v) {
|
|
const url = this._url;
|
|
|
|
if (v === "") {
|
|
url.query = null;
|
|
this._query._list = [];
|
|
return;
|
|
}
|
|
|
|
const input = v[0] === "?" ? v.substring(1) : v;
|
|
url.query = "";
|
|
usm.basicURLParse(input, { url, stateOverride: "query" });
|
|
this._query._list = urlencoded.parseUrlencodedString(input);
|
|
}
|
|
|
|
get searchParams() {
|
|
return this._query;
|
|
}
|
|
|
|
get hash() {
|
|
if (this._url.fragment === null || this._url.fragment === "") {
|
|
return "";
|
|
}
|
|
|
|
return `#${this._url.fragment}`;
|
|
}
|
|
|
|
set hash(v) {
|
|
if (v === "") {
|
|
this._url.fragment = null;
|
|
return;
|
|
}
|
|
|
|
const input = v[0] === "#" ? v.substring(1) : v;
|
|
this._url.fragment = "";
|
|
usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" });
|
|
}
|
|
|
|
toJSON() {
|
|
return this.href;
|
|
}
|
|
};
|