From c4066e9fe3a08167568e59efbbb61a758fa65159 Mon Sep 17 00:00:00 2001 From: Joseph Abbey Date: Tue, 23 Jan 2024 21:19:07 +0000 Subject: [PATCH] Enhanced web editor --- web/.gitignore | 1 + web/index.html | 260 +++++++++++++ web/jsconfig.json | 12 + web/main.js | 953 +++++++++++++++++++++++++++++++++++++++++++++ web/package.json | 19 + web/pnpm-lock.yaml | 676 ++++++++++++++++++++++++++++++++ web/types.d.ts | 3 + 7 files changed, 1924 insertions(+) create mode 100644 web/.gitignore create mode 100644 web/index.html create mode 100644 web/jsconfig.json create mode 100644 web/main.js create mode 100644 web/package.json create mode 100644 web/pnpm-lock.yaml create mode 100644 web/types.d.ts diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/web/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..eee68d0 --- /dev/null +++ b/web/index.html @@ -0,0 +1,260 @@ + + + + + GarminHomeAssistant + + + + + + +
+ + + + +
+ +
+ + +
+
+

GarminHomeAssistant Troubleshooting

+ +
+ +

+ This is a troubleshooting tool for the GarminHomeAssistant watch app. + It allows you to test your Home Assistant API connection. +

+ +
+
Check now!
+ +
+
+
Check now!
+ +
+
+
+ + + + + + + diff --git a/web/jsconfig.json b/web/jsconfig.json new file mode 100644 index 0000000..aa5d751 --- /dev/null +++ b/web/jsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "lib": ["esnext", "dom"], + "types": [ + "./node_modules/monaco-editor/monaco.d.ts", + "./node_modules/json-ast-comments/lib/index.d.ts", + "./node_modules/@types/toastify-js/index.d.ts", + "./types.d.ts" + ] + }, + "include": ["."] +} diff --git a/web/main.js b/web/main.js new file mode 100644 index 0000000..9db4fd9 --- /dev/null +++ b/web/main.js @@ -0,0 +1,953 @@ +/** + * Get all entities in HomeAssistant. + * @param {string} api_url + * @param {string} api_token + * @returns {Promise>} [id, name] + */ +async function get_entities(api_url, api_token) { + const res = await fetch(api_url + '/template', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: `{"template":"[{% for entity in states %}[\\"{{ entity.entity_id }}\\",\\"{{ entity.name }}\\"]{% if not loop.last %},{% endif %}{% endfor %}]"}`, + }); + if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } + return Object.fromEntries(await res.json()); +} + +/** + * Get all devices in HomeAssistant. + * @param {string} api_url + * @param {string} api_token + * @returns {Promise>} [id, name] + */ +async function get_devices(api_url, api_token) { + const res = await fetch(api_url + '/template', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: `{"template":"{% set devices = states | map(attribute='entity_id') | map('device_id') | unique | reject('eq', None) | list %}[{% for device in devices %}[\\"{{ device }}\\",\\"{{ device_attr(device, 'name') }}\\"]{% if not loop.last %},{% endif %}{% endfor %}]"}`, + }); + if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } + return Object.fromEntries(await res.json()); +} + +/** + * Get all areas in HomeAssistant. + * @param {string} api_url + * @param {string} api_token + * @returns {Promise>} [id, name] + */ +async function get_areas(api_url, api_token) { + const res = await fetch(api_url + '/template', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: `{"template":"[{% for area in areas() %}[\\"{{ area }}\\",\\"{{ area_name(area) }}\\"]{% if not loop.last %},{% endif %}{% endfor %}]"}`, + }); + if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } + return Object.fromEntries(await res.json()); +} + +/** + * Get all services in HomeAssistant. + * @param {string} api_url + * @param {string} api_token + * @returns {Promise<[string, { name: string; description: string; fields: Record }][]>} [id, data] + */ +async function get_services(api_url, api_token) { + const res = await fetch(api_url + '/services', { + method: 'GET', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + }); + if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } + const data = await res.json(); + const services = []; + for (const d of data) { + for (const service in d.services) { + services.push([`${d.domain}.${service}`, d.services[service]]); + } + } + return services; +} + +/** + * Get schema from GitHub. + * @returns {Promise<{}>} + */ +async function get_schema() { + const res = await fetch( + 'https://raw.githubusercontent.com/house-of-abbey/GarminHomeAssistant/main/config.schema.json' + ); + return res.json(); +} + +/** + * Generate schema for HomeAssistant. + * @param {Record} entities + * @param {Record} devices + * @param {Record} areas + * @param {[string, { name: string; description: string; fields: Record }][]} services + * @param {{}} schema + * @returns {Promise<{}>} + */ +async function generate_schema(entities, devices, areas, services, schema) { + schema.$defs.entity = { + enum: Object.keys(entities), + }; + schema.$defs.device = { + enum: Object.keys(devices), + }; + schema.$defs.area = { + enum: Object.keys(areas), + }; + + const oneOf = []; + for (const [id, data] of services) { + const i_properties = { + service: { + title: data.name, + description: data.description, + const: id, + }, + data: { + type: 'object', + properties: {}, + additionalProperties: false, + }, + }; + const required = []; + for (const [field, f] of Object.entries(data.fields)) { + i_properties.data.properties[field] = { + title: f.name, + description: f.description, + example: f.example, + }; + if (f.required) { + required.push(field); + } + + const selector = f.selector; + if (selector) { + if (Object.hasOwn(selector, 'action')) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + $ref: '#/$defs/tap_action', + }; + } else if (Object.hasOwn(selector, 'area')) { + if (selector.area?.multiple) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + $ref: '#/$defs/area', + }; + } else { + i_properties.data.properties[field].$ref = '#/$defs/area'; + } + } else if (Object.hasOwn(selector, 'boolean')) { + i_properties.data.properties[field].type = 'boolean'; + } else if (Object.hasOwn(selector, 'number')) { + i_properties.data.properties[field].type = 'number'; + i_properties.data.properties[field].minimum = selector.number?.min; + i_properties.data.properties[field].maximum = selector.number?.max; + i_properties.data.properties[field].multipleOf = + selector.number?.step; + } else if (Object.hasOwn(selector, 'color_temp')) { + i_properties.data.properties[field].type = 'number'; + i_properties.data.properties[field].minimum = + selector.color_temp?.min; + i_properties.data.properties[field].maximum = + selector.color_temp?.max; + i_properties.data.properties[field].multipleOf = + selector.color_temp?.step; + } else if (Object.hasOwn(selector, 'date')) { + i_properties.data.properties[field].type = 'string'; + i_properties.data.properties[field].pattern = + '^\\d{4}-\\d{2}-\\d{2}$'; + } else if (Object.hasOwn(selector, 'datetime')) { + i_properties.data.properties[field].type = 'string'; + i_properties.data.properties[field].pattern = + '^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}(\\:\\d{2})?$'; + } else if (Object.hasOwn(selector, 'time')) { + i_properties.data.properties[field].type = 'string'; + i_properties.data.properties[field].pattern = + '^\\d{2}:\\d{2}(\\:\\d{2})?$'; + } else if (Object.hasOwn(selector, 'device')) { + if (selector.device?.multiple) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + $ref: '#/$defs/device', + }; + } else { + i_properties.data.properties[field].$ref = '#/$defs/device'; + } + } else if (Object.hasOwn(selector, 'entity')) { + if (selector.entity?.multiple) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + $ref: '#/$defs/entity', + }; + } else { + i_properties.data.properties[field].$ref = '#/$defs/entity'; + } + } else if (Object.hasOwn(selector, 'icon')) { + i_properties.data.properties[field].type = 'string'; + i_properties.data.properties[field].pattern = '^[^.]+:[^.]+$'; + } else if (Object.hasOwn(selector, 'location')) { + i_properties.data.properties[field].type = 'object'; + i_properties.data.properties[field].properties = { + longitude: { + type: 'number', + }, + latitude: { + type: 'number', + }, + radius: { + type: 'number', + minimum: 0, + }, + }; + } else if (Object.hasOwn(selector, 'color_rgb')) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].prefixItems = [ + { + type: 'number', + minimum: 0, + maximum: 255, + multipleOf: 1, + }, + { + type: 'number', + minimum: 0, + maximum: 255, + multipleOf: 1, + }, + { + type: 'number', + minimum: 0, + maximum: 255, + multipleOf: 1, + }, + ]; + } else if (Object.hasOwn(selector, 'select')) { + const oneOf2 = []; + if (selector.select?.options) { + for (let o of selector.select.options) { + if (typeof o == 'string') { + oneOf2.push({ + const: o, + }); + } else { + oneOf2.push({ + const: o.value || '', + }); + } + } + } + if (selector.select?.custom) { + oneOf2.push({ + type: 'string', + }); + } + if (selector.select?.multiple) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + oneOf: oneOf2, + }; + } else { + i_properties.data.properties[field].oneOf = oneOf2; + } + } else if (Object.hasOwn(selector, 'state' in selector || 'template')) { + i_properties.data.properties[field].type = 'string'; + } else if (Object.hasOwn(selector, 'text')) { + let pattern; + const p = selector.text?.type; + if (p == 'color') { + pattern = '^#[0-9a-fA-F]{6}$'; + } else if (p == 'date') { + pattern = '^\\d{4}-\\d{2}-\\d{2}$'; + } else if (p == 'datetime-local') { + pattern = '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$'; + } else if (p == 'email') { + pattern = + '^([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22))*\\x40([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d))*$'; + } else if (p == 'month') { + pattern = '^\\d{4}-\\d{2}$'; + } else if (p == 'number') { + pattern = '^d*.?d+$'; + } else if (p == 'time') { + pattern = '^\\d{2}:\\d{2}$'; + } else if (p == 'url') { + pattern = + "^[a-z](?:[-a-z0-9\\+\\.])*:(?:\\/\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD])*@)?(?:\\[(?:(?:(?:[0-9a-f]{1,4}:){6}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|::(?:[0-9a-f]{1,4}:){5}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:[0-9a-f]{1,4}:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3})|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|v[0-9a-f]+[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:]+)\\]|(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\\.(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}|(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD])*)(?::[0-9]*)?(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD]))*)*|\\/(?:(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD]))*)*)?|(?:(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD]))+)(?:\\/(?:(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD]))*)*|(?!(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD])))(?:\\?(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\/\\?\\xA0-\\uD7FF\\uE000-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E\\uDB80-\\uDBBE\\uDBC0-\\uDBFE][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F\\uDBBF\\uDBFF][\\uDC00-\\uDFFD])*)?(?:\\#(?:%[0-9a-f][0-9a-f]|[-a-z0-9\\._~!\\$&'\\(\\)\\*\\+,;=:@\\/\\?\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[\\uD800-\\uD83E\\uD840-\\uD87E\\uD880-\\uD8BE\\uD8C0-\\uD8FE\\uD900-\\uD93E\\uD940-\\uD97E\\uD980-\\uD9BE\\uD9C0-\\uD9FE\\uDA00-\\uDA3E\\uDA40-\\uDA7E\\uDA80-\\uDABE\\uDAC0-\\uDAFE\\uDB00-\\uDB3E\\uDB44-\\uDB7E][\\uDC00-\\uDFFF]|[\\uD83F\\uD87F\\uD8BF\\uD8FF\\uD93F\\uD97F\\uD9BF\\uD9FF\\uDA3F\\uDA7F\\uDABF\\uDAFF\\uDB3F\\uDB7F][\\uDC00-\\uDFFD])*)?$"; + } else if (p == 'week') { + pattern = '^\\d{4}-W\\d{2}$'; + } + if (selector.text?.multiple) { + i_properties.data.properties[field].type = 'array'; + i_properties.data.properties[field].items = { + type: 'string', + pattern: pattern, + }; + } else { + i_properties.data.properties[field].type = 'string'; + i_properties.data.properties[field].pattern = pattern; + } + } + } + if (required.length > 0) { + i_properties.data.required = required; + } + } + oneOf.push({ + title: data.name, + description: data.description, + properties: i_properties, + }); + } + schema.$defs.tap_action = { + type: 'object', + oneOf: oneOf, + properties: { + service: { + type: 'string', + }, + confirm: { + $ref: '#/$defs/confirm', + }, + data: { + type: 'object', + properties: {}, + }, + }, + }; + delete schema.$defs.tap.properties.service; + delete schema.$schema; + + return schema; +} + +let api_url = localStorage.getItem('api_url') ?? ''; +let menu_url = localStorage.getItem('menu_url') ?? ''; +let api_token = localStorage.getItem('api_token') ?? ''; +document.querySelector('#api_url').value = api_url; +document.querySelector('#menu_url').value = menu_url; +document.querySelector('#api_token').value = api_token; + +document.querySelector('#troubleshooting').addEventListener('click', (e) => { + document.querySelector('#troubleshooting-dialog').showModal(); +}); + +document.querySelector('#test-api').addEventListener('click', async (e) => { + try { + document.querySelector('#test-api-response').innerText = 'Testing...'; + const res = await fetch(api_url + '/', { + headers: { + Authorization: `Bearer ${api_token}`, + }, + cache: 'no-cache', + mode: 'cors', + }); + const text = await res.text(); + if (res.status == 200) { + document.querySelector('#test-api-response').innerText = + JSON.parse(text).message; + } else if (res.status == 400) { + try { + document.querySelector('#test-api-response').innerText = + JSON.parse(text).message; + } catch { + document.querySelector('#test-api-response').innerText = text; + } + } else if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + document.querySelector('#test-api-response').innerText = 'Invalid token.'; + } else { + document.querySelector('#test-api-response').innerText = text; + } + } catch (e) { + document.querySelector('#test-api-response').innerText = e.message; + } +}); +document.querySelector('#test-menu').addEventListener('click', async (e) => { + try { + document.querySelector('#test-menu-response').innerText = 'Testing...'; + const res = await fetch(menu_url, { + cache: 'no-cache', + mode: 'cors', + }); + if (res.status == 200) { + document.querySelector('#test-menu-response').innerText = 'Available'; + } else if (res.status == 400) { + document.querySelector('#test-menu-response').innerText = + await res.text(); + } else { + document.querySelector('#test-menu-response').innerText = + await res.text(); + } + } catch (e) { + document.querySelector('#test-menu-response').innerText = e.message; + } +}); + +function get(d, p) { + for (let i = 0; i < p.length; i++) { + d = d[p[i]]; + } + return d; +} + +/** + * @param {{ text: string; color: string }} options + */ +function toast({ text, color }) { + const t = Toastify({ + text, + gravity: 'bottom', // `top` or `bottom` + position: 'right', // `left`, `center` or `right` + stopOnFocus: true, // Prevents dismissing of toast on hover + // close: true, + style: { + background: 'var(--ctp-mocha-base)', + outline: '1px solid ' + (color ?? 'var(--ctp-mocha-blue)'), + }, + }); + t.showToast(); + return t; +} + +// require is provided by loader.min.js. +require.config({ + paths: { + vs: 'https://unpkg.com/monaco-editor@0.45.0/min/vs', + }, +}); +require(['vs/editor/editor.main'], async () => { + let entities, devices, areas, services, schema; + async function loadSchema() { + [entities, devices, areas, services, schema] = await Promise.all([ + get_entities(api_url, api_token), + get_devices(api_url, api_token), + get_areas(api_url, api_token), + get_services(api_url, api_token), + get_schema(), + ]); + makeMarkers(); + try { + schema = await generate_schema( + entities, + devices, + areas, + services, + schema + ); + } catch {} + console.log(schema); + // configure the JSON language support with schemas and schema associations + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + schemas: [ + { + uri: 'https://raw.githubusercontent.com/house-of-abbey/GarminHomeAssistant/main/config.schema.json', + fileMatch: [modelUri.toString()], + schema, + }, + ], + }); + } + + document.querySelector('#api_url').addEventListener('change', (e) => { + api_url = e.target.value; + localStorage.setItem('api_url', api_url); + document.querySelector('#test-api-response').innerText = 'Check now!'; + loadSchema(); + }); + document.querySelector('#menu_url').addEventListener('change', (e) => { + menu_url = e.target.value; + localStorage.setItem('menu_url', menu_url); + document.querySelector('#test-menu-response').innerText = 'Check now!'; + checkRemoteMenu(); + }); + document.querySelector('#api_token').addEventListener('change', (e) => { + api_token = e.target.value; + localStorage.setItem('api_token', api_token); + document.querySelector('#api_token').classList.remove('invalid'); + document.querySelector('#test-api-response').innerText = 'Check now!'; + loadSchema(); + }); + loadSchema(); + checkRemoteMenu(); + + async function checkRemoteMenu() { + if (menu_url != '') { + const remote = await fetch(menu_url, { + cache: 'no-cache', + mode: 'cors', + }); + if (remote.status == 200) { + document.querySelector('#menu_url').classList.remove('invalid'); + const text = await remote.text(); + if (model.getValue() === text) { + document.querySelector('#menu_url').classList.remove('outofsync'); + } else { + document.querySelector('#menu_url').classList.add('outofsync'); + } + } else { + document.querySelector('#menu_url').classList.add('invalid'); + } + } + } + + // Configures two JSON schemas, with references. + + var modelUri = monaco.Uri.parse('/config/www/garmin/menu.json'); // a made up unique URI for our model + var model = monaco.editor.createModel( + localStorage.getItem('json') ?? '{}', + 'json', + modelUri + ); + + monaco.editor.defineTheme( + 'mocha', + await fetch( + 'https://josephabbey.github.io/catppuccin-monaco/mocha.json' + ).then((r) => r.json()) + ); + + const editor = monaco.editor.create(document.getElementById('container'), { + model: model, + theme: 'mocha', + automaticLayout: true, + glyphMargin: true, + }); + + window.addEventListener('keydown', (e) => { + if (e.key == 's' && e.ctrlKey) { + e.preventDefault(); + model.setValue( + JSON.stringify(JSON.parse(editor.getValue()), undefined, 2) + '\n' + ); + } + }); + + var decorations = editor.createDecorationsCollection([]); + + let markers = []; + + const renderTemplate = editor.addCommand( + 0, + async function (_, template) { + const t = toast({ + text: 'Rendering template...', + }); + const res = await fetch(api_url + '/template', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: `{"template":"${template}"}`, + }); + t.hideToast(); + if (res.status == 200) { + toast({ + text: await res.text(), + color: 'var(--ctp-mocha-green)', + }); + } else if (res.status == 400) { + toast({ + text: (await res.json()).message, + color: 'var(--ctp-mocha-red)', + }); + } else if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } else { + toast({ + text: await res.text(), + color: 'var(--ctp-mocha-red)', + }); + } + }, + '' + ); + + const runAction = editor.addCommand( + 0, + async function (_, action) { + const service = action.tap_action.service.split('.'); + let data = action.tap_action.data; + if (data) { + data.entity_id = action.entity; + } else { + data = { + entity_id: action.entity, + }; + } + const t = toast({ + text: 'Running action...', + }); + const res = await fetch( + api_url + '/services/' + service[0] + '/' + service[1], + { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: JSON.stringify(data), + } + ); + t.hideToast(); + if (res.status == 200) { + toast({ + text: 'Success', + color: 'var(--ctp-mocha-green)', + }); + } else if (res.status == 400) { + const text = await res.text(); + try { + toast({ + text: JSON.parse(text).message, + color: 'var(--ctp-mocha-red)', + }); + } catch { + toast({ + text: text, + color: 'var(--ctp-mocha-red)', + }); + } + } else if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } else { + toast({ + text: await res.text(), + color: 'var(--ctp-mocha-red)', + }); + } + makeMarkers(); + }, + '' + ); + + const toggle = editor.addCommand( + 0, + async function (_, item) { + const entity = item.entity.split('.'); + const t = toast({ + text: 'Toggling...', + }); + const res = await fetch(api_url + '/services/' + entity[0] + '/toggle', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: JSON.stringify({ + entity_id: item.entity, + }), + }); + t.hideToast(); + if (res.status == 200) { + toast({ + text: 'Success', + color: 'var(--ctp-mocha-green)', + }); + } else if (res.status == 400) { + const text = await res.text(); + try { + toast({ + text: JSON.parse(text).message, + color: 'var(--ctp-mocha-red)', + }); + } catch { + toast({ + text: text, + color: 'var(--ctp-mocha-red)', + }); + } + } else if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } else { + toast({ + text: await res.text(), + color: 'var(--ctp-mocha-red)', + }); + } + makeMarkers(); + }, + '' + ); + + async function makeMarkers() { + try { + const ast = json.parse(model.getValue()); + const data = JSON.parse(model.getValue()); + markers = []; + const glyphs = []; + async function testToggle(range, entity) { + const res = await fetch(api_url + '/states/' + entity, { + method: 'GET', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + }); + const d = await res.json(); + if (d.state == 'on') { + decorations.append([ + { + range, + options: { + isWholeLine: true, + glyphMarginClassName: 'toggle_on', + }, + }, + ]); + } else { + decorations.append([ + { + range, + options: { + isWholeLine: true, + glyphMarginClassName: 'toggle_off', + }, + }, + ]); + } + } + const toggles = []; + async function testTemplate(range, template) { + const l = model.getValueInRange(range); + let trim = 0; + while (trim < l.length && l[trim] == ' ') { + trim++; + } + trim++; + const res = await fetch(api_url + '/template', { + method: 'POST', + headers: { + Authorization: `Bearer ${api_token}`, + }, + mode: 'cors', + body: `{"template":"${template}"}`, + }); + if (res.status == 200) { + markers.push({ + message: await res.text(), + severity: monaco.MarkerSeverity.Info, + ...range, + startColumn: trim, + }); + } else if (res.status == 400) { + markers.push({ + message: (await res.json()).message, + severity: monaco.MarkerSeverity.Error, + ...range, + startColumn: trim, + }); + } else if (res.status == 401 || res.status == 403) { + document.querySelector('#api_token').classList.add('invalid'); + } else { + markers.push({ + message: res.statusText, + severity: monaco.MarkerSeverity.Error, + ...range, + startColumn: trim, + }); + } + } + const templates = []; + /** + * @param {import('json-ast-comments').JsonAst | import('json-ast-comments').JsonProperty} node + * @param {string[]} path + */ + function recurse(node, path) { + if (node.type === 'property') { + if (node.key[0].value === 'content') { + templates.push([ + { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.value[0].range.end.line + 1, + endColumn: 10000, + }, + node.value[0].value, + ]); + } else if (entities != null && node.key[0].value === 'entity') { + const range = { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.value[0].range.end.line + 1, + endColumn: 10000, + }; + const l = model.getValueInRange(range); + let trim = 0; + while (trim < l.length && l[trim] == ' ') { + trim++; + } + trim++; + markers.push({ + message: entities[node.value[0].value] ?? 'Entity not found', + severity: monaco.MarkerSeverity.Hint, + ...range, + startColumn: trim, + }); + } else if (node.key[0].value === 'type') { + if (node.value[0].value === 'toggle') { + toggles.push([ + { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.value[0].range.end.line + 1, + endColumn: 10000, + }, + get(data, path).entity, + ]); + } else { + glyphs.push({ + range: { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.value[0].range.end.line + 1, + endColumn: 10000, + }, + options: { + isWholeLine: true, + glyphMarginClassName: node.value[0].value, + }, + }); + } + } else { + recurse(node.value[0], [...path, node.key[0].value]); + } + } else if (node.type === 'array') { + for (let i = 0; i < node.members.length; i++) { + recurse(node.members[i], [...path, i]); + } + } else if (node.type === 'object') { + for (let member of node.members) { + recurse(member, path); + } + } + } + recurse(ast.body[0], []); + decorations.clear(); + decorations.append(glyphs); + await Promise.all(templates.map((t) => testTemplate(...t))); + toggles.forEach((t) => testToggle(...t)); + monaco.editor.setModelMarkers(model, 'template', markers); + } catch {} + } + makeMarkers(); + + model.onDidChangeContent(async function () { + localStorage.setItem('json', model.getValue()); + makeMarkers(); + }); + + monaco.languages.registerCodeLensProvider('json', { + provideCodeLenses: function (model, token) { + const lenses = []; + try { + const ast = json.parse(model.getValue()); + const data = JSON.parse(model.getValue()); + /** + * @param {import('json-ast-comments').JsonAst | import('json-ast-comments').JsonProperty} node + * @param {string[]} path + */ + function recurse(node, path) { + if (node.type === 'property') { + if (node.key[0].value === 'tap_action') { + const d = get(data, path); + if (d.tap_action.service) { + lenses.push({ + range: { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.key[0].range.start.line + 1, + endColumn: 0, + }, + id: Math.random().toString(36).substring(7), + command: { + id: runAction, + title: 'Run Action', + arguments: [d], + }, + }); + } else { + recurse(node.value[0], [...path, node.key[0].value]); + } + } else if (node.key[0].value === 'content') { + lenses.push({ + range: { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.key[0].range.start.line + 1, + endColumn: 0, + }, + id: Math.random().toString(36).substring(7), + command: { + id: renderTemplate, + title: 'Render Template', + arguments: [node.value[0].value], + }, + }); + } else if ( + node.key[0].value === 'type' && + node.value[0].value === 'toggle' + ) { + lenses.push({ + range: { + startLineNumber: node.key[0].range.start.line + 1, + startColumn: 0, + endLineNumber: node.key[0].range.start.line + 1, + endColumn: 0, + }, + id: Math.random().toString(36).substring(7), + command: { + id: toggle, + title: 'Toggle', + arguments: [get(data, path)], + }, + }); + } else { + recurse(node.value[0], [...path, node.key[0].value]); + } + } else if (node.type === 'array') { + for (let i = 0; i < node.members.length; i++) { + recurse(node.members[i], [...path, i]); + } + } else if (node.type === 'object') { + for (let member of node.members) { + recurse(member, path); + } + } + } + recurse(ast.body[0], []); + } catch {} + return { + lenses, + dispose: () => {}, + }; + }, + resolveCodeLens: function (model, codeLens, token) { + return codeLens; + }, + }); +}); diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..dd5300c --- /dev/null +++ b/web/package.json @@ -0,0 +1,19 @@ +{ + "name": "web", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "serve .." + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/toastify-js": "^1.12.3", + "@vscode/webview-ui-toolkit": "1.4.0", + "json-ast-comments": "1.1.1", + "monaco-editor": "0.45.0", + "serve": "^14.2.1" + } +} \ No newline at end of file diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml new file mode 100644 index 0000000..ec6251d --- /dev/null +++ b/web/pnpm-lock.yaml @@ -0,0 +1,676 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@types/toastify-js': + specifier: ^1.12.3 + version: 1.12.3 + '@vscode/webview-ui-toolkit': + specifier: 1.4.0 + version: 1.4.0(react@18.2.0) + json-ast-comments: + specifier: 1.1.1 + version: 1.1.1 + monaco-editor: + specifier: 0.45.0 + version: 0.45.0 + serve: + specifier: ^14.2.1 + version: 14.2.1 + +packages: + + /@microsoft/fast-element@1.12.0: + resolution: {integrity: sha512-gQutuDHPKNxUEcQ4pypZT4Wmrbapus+P9s3bR/SEOLsMbNqNoXigGImITygI5zhb+aA5rzflM6O8YWkmRbGkPA==} + dev: true + + /@microsoft/fast-foundation@2.49.4: + resolution: {integrity: sha512-5I2tSPo6bnOfVAIX7XzX+LhilahwvD7h+yzl3jW0t5IYmMX9Lci9VUVyx5f8hHdb1O9a8Y9Atb7Asw7yFO/u+w==} + dependencies: + '@microsoft/fast-element': 1.12.0 + '@microsoft/fast-web-utilities': 5.4.1 + tabbable: 5.3.3 + tslib: 1.14.1 + dev: true + + /@microsoft/fast-react-wrapper@0.3.22(react@18.2.0): + resolution: {integrity: sha512-XhlX4m6znh7XW92oPvlKoG9USUn9JtF9rP1qtUoIbkaDaFtUS+H8o1Jn6/oK/rS44LbBLJXrvRkInmSWlDiGFw==} + peerDependencies: + react: '>=16.9.0' + dependencies: + '@microsoft/fast-element': 1.12.0 + '@microsoft/fast-foundation': 2.49.4 + react: 18.2.0 + dev: true + + /@microsoft/fast-web-utilities@5.4.1: + resolution: {integrity: sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==} + dependencies: + exenv-es6: 1.1.1 + dev: true + + /@types/toastify-js@1.12.3: + resolution: {integrity: sha512-9RjLlbAHMSaae/KZNHGv19VG4gcLIm3YjvacCXBtfMfYn26h76YP5oxXI8k26q4iKXCB9LNfv18lsoS0JnFPTg==} + dev: true + + /@vscode/webview-ui-toolkit@1.4.0(react@18.2.0): + resolution: {integrity: sha512-modXVHQkZLsxgmd5yoP3ptRC/G8NBDD+ob+ngPiWNQdlrH6H1xR/qgOBD85bfU3BhOB5sZzFWBwwhp9/SfoHww==} + peerDependencies: + react: '>=16.9.0' + dependencies: + '@microsoft/fast-element': 1.12.0 + '@microsoft/fast-foundation': 2.49.4 + '@microsoft/fast-react-wrapper': 0.3.22(react@18.2.0) + react: 18.2.0 + tslib: 2.6.2 + dev: true + + /@zeit/schemas@2.29.0: + resolution: {integrity: sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==} + dev: true + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: true + + /ajv@8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /boxen@7.0.0: + resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==} + engines: {node: '>=14.16'} + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.0.1 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + dev: true + + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: true + + /chalk-template@0.4.0: + resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} + engines: {node: '>=12'} + dependencies: + chalk: 4.1.2 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chalk@5.0.1: + resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: true + + /clipboardy@3.0.0: + resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + arch: 2.2.0 + execa: 5.1.1 + is-wsl: 2.2.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /compression@1.7.4: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /content-disposition@0.5.2: + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} + engines: {node: '>= 0.6'} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /exenv-es6@1.1.1: + resolution: {integrity: sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-url-parser@1.1.3: + resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + dependencies: + punycode: 1.4.1 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-port-reachable@4.0.0: + resolution: {integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /json-ast-comments@1.1.1: + resolution: {integrity: sha512-UOHlf7ns5t1GiI3+T5tf9SN2OepXTo/sqhd+cQj++DaUMKQOOCbX+eRlIoHcEv9m902reDTc1mCJD4J69xFJSg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /mime-db@1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types@2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.33.0 + dev: true + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /monaco-editor@0.45.0: + resolution: {integrity: sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA==} + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-to-regexp@2.2.1: + resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + dev: true + + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /range-parser@1.2.0: + resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} + engines: {node: '>= 0.6'} + dev: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: true + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: true + + /registry-auth-token@3.3.2: + resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + dev: true + + /registry-url@3.1.0: + resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} + engines: {node: '>=0.10.0'} + dependencies: + rc: 1.2.8 + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /serve-handler@6.1.5: + resolution: {integrity: sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==} + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + fast-url-parser: 1.1.3 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 2.2.1 + range-parser: 1.2.0 + dev: true + + /serve@14.2.1: + resolution: {integrity: sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==} + engines: {node: '>= 14'} + hasBin: true + dependencies: + '@zeit/schemas': 2.29.0 + ajv: 8.11.0 + arg: 5.0.2 + boxen: 7.0.0 + chalk: 5.0.1 + chalk-template: 0.4.0 + clipboardy: 3.0.0 + compression: 1.7.4 + is-port-reachable: 4.0.0 + serve-handler: 6.1.5 + update-check: 1.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /tabbable@5.3.3: + resolution: {integrity: sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==} + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: true + + /update-check@1.5.4: + resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true diff --git a/web/types.d.ts b/web/types.d.ts new file mode 100644 index 0000000..a5d4f97 --- /dev/null +++ b/web/types.d.ts @@ -0,0 +1,3 @@ +declare namespace json { + export function parse(text: string): import('json-ast-comments').JsonDocument; +}