diff --git a/README.md b/README.md index c93b88d..024e07a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,45 @@ # contacts-pane -Contact management: AddressBooks, Groups, Individuals and Organizations. +SolidOS pane that displays a personal contact and address books. + +![CI](https://github.com/SolidOS/contacts-pane/workflows/CI/badge.svg) + +## Contribute + +### Tech stack + +- JavaScript +- Jest +- Eslint +- SolidOS + +### Tests + +To run all tests: +```shell script +npm test +``` + +If you are a first time developer/user on Windows 10, the repository may give package issues regarding webpack or jest. +If this is the case, simply run "npm audit fix" and upgrade the repository. It should work fine. + +#### Unit tests + +Unit tests use `jest` and are placed in the `test` folder. + +### Dev Server + +Start a webpack dev server: + +```shell script +npm start +``` + +Visit `http://localhost:8080/` to render the pane. Adjust `const webIdToShow` in `./dev/index.ts` to show a different profile. + +### Build + +``` +npm run build +``` + diff --git a/babel.config.mjs b/babel.config.mjs index 7de803b..f15d0aa 100644 --- a/babel.config.mjs +++ b/babel.config.mjs @@ -14,8 +14,7 @@ export default { [ 'babel-plugin-inline-import', { extensions: [ - '.ttl', - '.sparql' + '.ttl' ] } ] diff --git a/declarations.d.ts b/declarations.d.ts index 5cc7479..b205565 100644 --- a/declarations.d.ts +++ b/declarations.d.ts @@ -3,7 +3,7 @@ declare module '*.ttl' { export default content; } -declare module '*.sparql' { +declare module '*.css' { const content: string; export default content; } diff --git a/dev/context.ts b/dev/context.ts new file mode 100644 index 0000000..7faa6c9 --- /dev/null +++ b/dev/context.ts @@ -0,0 +1,27 @@ +import { default as pane } from '../src/contactsPane' +import { DataBrowserContext, PaneRegistry } from 'pane-registry' +import { solidLogicSingleton, store } from 'solid-logic' +import { LiveStore } from 'rdflib' + +// Configure fetcher for development +if (store.fetcher) { + // Configure for cross-origin requests + (store.fetcher as any).crossSite = true; + (store.fetcher as any).withCredentials = false; +} + +export const context: DataBrowserContext = { + session: { + store: store as LiveStore, + paneRegistry: { + byName: (name: string) => { + return pane + } + } as PaneRegistry, + logic: solidLogicSingleton + }, + dom: document, + getOutliner: () => null, +} + +export const fetcher = store.fetcher diff --git a/dev/dev-global.css b/dev/dev-global.css new file mode 100644 index 0000000..567a1b3 --- /dev/null +++ b/dev/dev-global.css @@ -0,0 +1,388 @@ +/* ---ONLY FOR LOCAL DEV--- */ +/* ---final version is in mashlib--- */ + +/* Global CSS: base styles, variables, and resets */ +/* Accessible color palette */ + +:root { + /* Primary/Accent colors (profile-pane specific) */ + --color-primary: #7C4DFF; /* Vivid Purple */ + --color-secondary: #0077B6; /* Accessible Blue */ + --color-accent: #FFD600; /* Bright Yellow */ + --color-error: #B00020; /* Accessible Red */ + --color-success: #00C853; /* Accessible Green */ + + /* Card/Section backgrounds */ + --color-background: #FFFFFF; /* White */ + --color-card-bg: #FFFFFF; /* White for inner cards */ + --color-section-bg: #F5F5F5; /* Light grey for outer sections */ + + --color-text: #1A1A1A; /* Near-black */ + --color-text-secondary: #666; /* Added for repeated usage */ + --color-text-muted: #444; /* Added for repeated usage */ + --color-border-pale: #eee; /* Added for repeated borders */ + --color-info-bg: #ccccff; + --border-radius-full: 1em; /* Matches module usage */ + --border-radius-base: 0.5em; + --box-shadow: 0 2px 8px rgba(124,77,255,0.08); /* Matches module usage */ + --box-shadow-sm: 0 1px 4px rgba(124,77,255,0.12); + --spacing-xs: 0.5em; + --spacing-sm: 0.75em; + --spacing-md: 1em; + --spacing-lg: 1.5em; + --spacing-xl: 2em; + --font-family-base: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + --font-size-base: 1em; /* 16px default */ + --font-size-sm: 0.875em; /* 14px */ + --font-size-lg: 1.125em; /* 18px */ + --font-size-xl: 1.25em; /* 20px */ + --line-height-base: 1.5; /* WCAG recommended */ + --line-height-tight: 1.4; + --line-height-loose: 1.6; + --letter-spacing-wide: 0.025em; + + /* Minimum font sizes for accessibility */ + --min-font-size: 14px; + --min-line-height: 1.4; + + /* Accessibility improvements */ + --min-touch-target: 44px; /* WCAG minimum touch target */ + --focus-ring-width: 2px; + --animation-duration: 0.2s; /* Reduced motion friendly */ + + /* Additional accessibility variables */ + --focus-indicator-width: 3px; + --animation-duration-slow: 0.3s; + --high-contrast-ratio: 7:1; /* WCAG AAA standard */ +} + + +/* Improve text rendering */ +html, body { + margin: 0; + padding: 0; + font-family: var(--font-family-base); + font-size: var(--font-size-base); + line-height: var(--line-height-base); + background: var(--color-background); + color: var(--color-text); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +/* Improved heading hierarchy */ +h1, h2, h3, h4, h5, h6 { + color: var(--color-primary); + font-weight: 600; + line-height: var(--line-height-tight); + margin-top: 0; + margin-bottom: var(--spacing-sm); +} + +h1 { font-size: 2em; } /* 32px */ +h2 { font-size: 1.5em; } /* 24px */ +h3 { font-size: 1.25em; } /* 20px */ +h4 { font-size: 1.125em; }/* 18px */ +h5, h6 { font-size: 1em; }/* 16px */ + +/* Better paragraph spacing */ +p { + margin-bottom: var(--spacing-md); + line-height: var(--line-height-base); + max-width: 65ch; /* Optimal reading width */ +} + +/* Improved link accessibility */ +a { + color: var(--color-primary); + text-decoration: underline; + text-underline-offset: 0.125em; + text-decoration-thickness: 0.0625em; +} + +a:hover, a:focus { + text-decoration-thickness: 0.125em; +} + +/* Ensure minimum font sizes are respected */ +@media screen and (max-width: 768px) { + html { + font-size: max(16px, 1rem); /* Never smaller than 16px on mobile */ + } +} + +/* Support for larger text preferences */ +@media (prefers-reduced-motion: no-preference) { + html { + scroll-behavior: smooth; + } +} + +/* Accessibility: focus styles */ +:focus { + outline: 2px solid var(--color-primary); + outline-offset: 1px; + box-shadow: 0 0 0 1px var(--color-background); +} + +/* Accessibility: Respect user motion preferences */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* Accessibility: High contrast mode support */ +@media (prefers-contrast: high) { + :root { + --color-border-pale: #000; + --box-shadow: 0 2px 4px rgba(0,0,0,0.5); + --box-shadow-sm: 0 1px 2px rgba(0,0,0,0.3); + } +} + +/* Accessibility: Improved focus management */ +:focus-visible { + outline: var(--focus-ring-width) solid var(--color-primary); + outline-offset: 2px; + box-shadow: 0 0 0 1px var(--color-background); +} + +:focus:not(:focus-visible) { + outline: none; +} + +/* Skip link for screen readers */ +.skip-link { + position: absolute; + top: -40px; + left: 6px; + background: var(--color-primary); + color: white; + padding: var(--spacing-sm) var(--spacing-md); + text-decoration: none; + border-radius: var(--border-radius-base); + z-index: 1000; + font-weight: 600; + font-size: var(--font-size-base); + line-height: 1; +} + +.skip-link:focus { + top: 6px; + outline: 2px solid white; + outline-offset: 2px; +} + +/* Semantic HTML5 improvements */ +article, aside, section { + display: block; +} + +header { + margin-bottom: var(--spacing-md); +} + +nav { + display: block; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; +} + +/* Enhanced keyboard navigation */ +*:focus-visible { + outline: var(--focus-indicator-width) solid var(--color-primary); + outline-offset: 2px; + box-shadow: 0 0 0 1px var(--color-background), 0 0 0 4px rgba(124, 77, 255, 0.2); + border-radius: 2px; + transition: none; /* Remove transitions on focus for immediate feedback */ +} + +/* Improve focus management for interactive elements */ +[role="button"]:focus, +[role="link"]:focus, +button:focus, +a:focus { + outline: 2px solid var(--color-primary); + outline-offset: 2px; + box-shadow: 0 0 0 1px var(--color-background); +} + +/* Enhanced error message accessibility */ +[role="alert"] { + padding: var(--spacing-md); + border: 2px solid var(--color-error); + border-radius: var(--border-radius-base); + background-color: rgba(176, 0, 32, 0.1); + margin: var(--spacing-md) 0; +} + +/* Success message styling */ +[role="status"] { + padding: var(--spacing-md); + border: 2px solid var(--color-success); + border-radius: var(--border-radius-base); + background-color: rgba(0, 200, 83, 0.1); + margin: var(--spacing-md) 0; +} + +/* Enhanced table accessibility */ +table { + border-collapse: collapse; + width: 100%; +} + +th { + background-color: var(--color-section-bg); + font-weight: 600; + text-align: left; + padding: var(--spacing-sm); +} + +td { + padding: var(--spacing-sm); +} + +/* Focus trap for modals */ +.focus-trap { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 9999; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; +} + +/* Enhanced button accessibility */ +button, [role="button"] { + cursor: pointer; + border: none; + border-radius: var(--border-radius-base); + padding: var(--spacing-sm) var(--spacing-md); + min-height: var(--min-touch-target); + min-width: var(--min-touch-target); + font-size: var(--font-size-base); + font-weight: 600; + transition: all var(--animation-duration) ease; + position: relative; +} + +button:disabled, [role="button"][aria-disabled="true"] { + opacity: 0.6; + cursor: not-allowed; + pointer-events: none; +} + +/* Loading indicator accessibility */ +.loading-spinner { + width: 40px; + height: 40px; + border: 3px solid var(--color-border-pale); + border-top: 3px solid var(--color-primary); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@media (prefers-reduced-motion: reduce) { + .loading-spinner { + animation: none; + border-top-color: var(--color-primary); + } +} + +/* Utility classes */ +.u-flex { + display: flex; +} +.u-grid { + display: grid; +} +.u-center { + justify-content: center; + align-items: center; +} +.u-gap { + gap: 1em; +} + +/* Common card component - used across all modules */ +.module-card { + background: var(--color-card-bg); + border-radius: var(--border-radius-full); + box-shadow: var(--box-shadow); + padding: var(--spacing-lg); + margin-bottom: var(--spacing-lg); + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +/* Common header styles */ +.module-header { + text-align: center; + margin-bottom: var(--spacing-md); +} + +/* Common flex patterns */ +.flex-center { + display: flex; + justify-content: center; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-column-center { + display: flex; + flex-direction: column; + align-items: center; +} + +/* Text utilities */ +.text-center { + text-align: center; +} + +.text-secondary { + color: var(--color-text-secondary); +} + +.text-muted { + color: var(--color-text-muted); +} + +/* Override solid-ui error message close button styling */ +.errorMessageBlock .close, +.errorMessageBlock button[type="button"], +.errorMessageBlock .button { + background: var(--color-border-pale) !important; + color: var(--color-text) !important; + border: 1px solid var(--color-border-pale) !important; +} + +.errorMessageBlock .close:hover, +.errorMessageBlock button[type="button"]:hover, +.errorMessageBlock .button:hover { + background: var(--color-text-secondary) !important; + color: var(--color-background) !important; +} \ No newline at end of file diff --git a/dev/index.html b/dev/index.html new file mode 100644 index 0000000..3b5ca7d --- /dev/null +++ b/dev/index.html @@ -0,0 +1,57 @@ + + + + + contacts-pane dev server + + + + + + + + +

pane under development

+
+
+
+
+ +
Rendering...
+ + diff --git a/dev/index.ts b/dev/index.ts new file mode 100644 index 0000000..47babea --- /dev/null +++ b/dev/index.ts @@ -0,0 +1,32 @@ +import { sym } from 'rdflib' +import { default as pane } from '../src/contactsPane' +import './dev-global.css' // Import after src to override component styles +import { context, fetcher } from './context' +import { authn, authSession } from 'solid-logic' +import * as UI from 'solid-ui' + +const loginBanner = document.getElementById('loginBanner') +const webId = document.getElementById('webId') + +loginBanner.appendChild(UI.login.loginStatusBox(document, null, {})) + +//const webIdToShow = 'https://solidos.solidcommunity.net/Contacts/index.ttl#this' //example to render an Address Book instead +const webIdToShow = 'https://testingsolidos.solidcommunity.net/profile/card#me' + +async function finishLogin() { + await authSession.handleIncomingRedirect({ restorePreviousSession: true }) + const session = authSession + if (session.info.isLoggedIn) { + // Update the page with the status. + webId.textContent = 'Logged in as: ' + authn.currentUser().uri + } else { + webId.textContent = '' + } + fetcher.load(webIdToShow).then(() => { + const app = pane.render(sym(webIdToShow), context) + document.getElementById('app').replaceWith(app) +}) +} + +finishLogin() + diff --git a/exampleOfOpenData/mit-wikidata-details.ttl b/exampleOfOpenData/mit-wikidata-details.ttl deleted file mode 100644 index 82b4893..0000000 --- a/exampleOfOpenData/mit-wikidata-details.ttl +++ /dev/null @@ -1,104 +0,0 @@ -@prefix rdf: . -@prefix rdfs: . -@prefix sesame: . -@prefix owl: . -@prefix xsd: . -@prefix fn: . -@prefix foaf: . -@prefix dc: . -@prefix hint: . -@prefix bd: . -@prefix bds: . -@prefix psn: . -@prefix pqn: . -@prefix prn: . -@prefix mwapi: . -@prefix gas: . -@prefix wdt: . -@prefix wdtn: . -@prefix psv: . -@prefix ps: . -@prefix pqv: . -@prefix pq: . -@prefix prv: . -@prefix pr: . -@prefix wdno: . -@prefix p: . -@prefix wikibase: . -@prefix wd: . -@prefix wdata: . -@prefix wds: . -@prefix wdv: . -@prefix wdref: . -@prefix schema: . -@prefix prov: . -@prefix skos: . -@prefix geo: . -@prefix geof: . -@prefix mediawiki: . -@prefix ontolex: . -@prefix dct: . - -wd:Q49108 schema:logo , ; - schema:subOrganization wd:Q142740 . - -wd:Q142740 schema:name "Sloan School of Management"@fr . - -wd:Q49108 schema:location wd:Q49111 . - -wd:Q49111 wdt:P625 "Point(-71.106111111 42.375)"^^geo:wktLiteral ; - schema:country wd:Q30 . - -wd:Q30 schema:name "États-Unis"@fr . - -wd:Q49108 schema:subOrganization wd:Q7185824 . - -wd:Q7185824 schema:name "Phillip T. and Susan M. Ragon Institute"@en . - -wd:Q49108 schema:subOrganization wd:Q3138607 . - -wd:Q3138607 schema:name "Lincoln Laboratory"@fr . - -wd:Q49108 schema:subOrganization wd:Q6716306 . - -wd:Q6716306 schema:name "Musée du MIT"@fr . - -wd:Q49108 schema:subOrganization wd:Q72855414 . - -wd:Q72855414 schema:name "Bates Research and Engineering Center"@en . - -wd:Q49108 schema:subOrganization wd:Q6801414 . - -wd:Q6801414 schema:name "McGovern Institute for Brain Research"@en . - -wd:Q49108 schema:subOrganization wd:Q7191073 . - -wd:Q7191073 schema:name "Picower Institute for Learning and Memory"@fr . - -wd:Q49108 schema:subOrganization wd:Q5025669 . - -wd:Q5025669 schema:name "Cambridge–MIT Institute"@en . - -wd:Q49108 schema:subOrganization wd:Q16977777 . - -wd:Q16977777 schema:name "MIT School of Humanities, Arts, and Social Sciences"@en . - -wd:Q49108 schema:subOrganization wd:Q45136595 . - -wd:Q45136595 schema:name "Deshpande Center for Technological Innovation, Massachusetts Institute of Technology"@en . - -wd:Q49108 schema:subOrganization wd:Q96391217 . - -wd:Q96391217 schema:name "MIT Schwarzman College of Computing"@en . - -wd:Q49108 schema:subOrganization wd:Q36874935 . - -wd:Q36874935 schema:name "Novartis-MIT Center for Continuous Manufacturing"@en . - -wd:Q49108 schema:subOrganization wd:Q6784300 . - -wd:Q6784300 schema:name "Massachusetts Institute of Technology School of Science"@en . - -wd:Q49108 schema:subOrganization wd:Q6716318 . - -wd:Q6716318 schema:name "MIT School of Architecture and Planning"@en . diff --git a/exampleOfOpenData/mit-wikidata-query.sparql b/exampleOfOpenData/mit-wikidata-query.sparql deleted file mode 100644 index b8031ad..0000000 --- a/exampleOfOpenData/mit-wikidata-query.sparql +++ /dev/null @@ -1,37 +0,0 @@ -CONSTRUCT -{ wd:Q49108 schema:name ?itemLabel; - schema:logo ?logo; - schema:logo ?sealImage; - schema:bogus ?bogus; - schema:subOrganization ?subsidiary . - ?subsidiary schema:name ?subsidiaryLabel . - ?supersidiary schema:subOrganization wd:Q49108 . - ?supersidiary schema:name ?supersidiaryLabel . - wd:Q49108 schema:location ?location . - ?location schema:elevation ?elevation . - ?location wdt:P131 ?region . ?region schema:name ?regionLabel . - ?location wdt:P625 ?coordinates . - ?location schema:country ?country . ?country schema:name ?countryLabel . -} -WHERE -{ - wd:Q49108 # rdfs:label ?itemLabel ; - wdt:P154 ?logo; - wdt:P158 ?sealImage . - OPTIONAL { wd:Q49108 wdt:P999 ?bogus; } - optional { wd:Q49108 wdt:P18 ?image } - - optional { wd:Q49108 wdt:P355 ?subsidiary . } - optional { ?supersidiary wdt:P355 wd:Q49108. } - - optional { wd:Q49108 wdt:P276 ?location . - - optional { ?location schema:eleveation ?elevation } - optional { ?location wdt:P131 ?adminstartiveRegion } - optional { ?location wdt:P625 ?coordinates } -optional { ?location wdt:P17 ?country } - } - - SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en,de,it". } -} -#ends diff --git a/exampleOfOpenData/wikidata-get.json b/exampleOfOpenData/wikidata-get.json deleted file mode 100644 index 7e6e697..0000000 --- a/exampleOfOpenData/wikidata-get.json +++ /dev/null @@ -1 +0,0 @@ -{"entities":{"Q1264942":{"pageid":1205676,"ns":0,"title":"Q1264942","lastrevid":1324719677,"modified":"2020-12-18T11:40:39Z","type":"item","id":"Q1264942","labels":{"ru":{"language":"ru","value":"\u0414\u0443\u043c\u0431\u0430\u0440\u0442\u043e\u043d-\u041e\u043a\u0441"},"fr":{"language":"fr","value":"Dumbarton Oaks"},"en":{"language":"en","value":"Dumbarton Oaks"},"zh":{"language":"zh","value":"\u6566\u5df4\u9813\u6a61\u6a39\u5712"},"pt":{"language":"pt","value":"Dumbarton Oaks"},"ca":{"language":"ca","value":"Dumbarton Oaks"},"de":{"language":"de","value":"Dumbarton Oaks"},"tr":{"language":"tr","value":"Dumbarton Oaks"},"it":{"language":"it","value":"Dumbarton Oaks"},"es":{"language":"es","value":"Dumbarton Oaks"},"pl":{"language":"pl","value":"Dumbarton Oaks"},"he":{"language":"he","value":"\u05d3\u05de\u05d1\u05e8\u05d8\u05d5\u05df \u05d0\u05d5\u05e7\u05e1"},"da":{"language":"da","value":"Dumbarton Oaks"},"nl":{"language":"nl","value":"Dumbarton Oaks"},"ja":{"language":"ja","value":"\u30c0\u30f3\u30d0\u30fc\u30c8\u30f3\u30fb\u30aa\u30fc\u30af\u30b9\u90b8"},"id":{"language":"id","value":"Dumbarton Oaks"},"ast":{"language":"ast","value":"Dumbarton Oaks"},"be":{"language":"be","value":"\u0414\u0443\u043c\u0431\u0430\u0440\u0442\u0430\u043d-\u041e\u043a\u0441"},"sr":{"language":"sr","value":"\u0414\u0430\u043c\u0431\u0435\u0440\u0442\u043e\u043d \u041e\u0443\u043a\u0441"},"fa":{"language":"fa","value":"\u062f\u0627\u0645\u0628\u0631\u062a\u0648\u0646 \u0627\u06a9\u0633"},"sv":{"language":"sv","value":"Dumbarton Oaks"},"ka":{"language":"ka","value":"\u10d3\u10e3\u10db\u10d1\u10d0\u10e0\u10e2\u10dd\u10dc-\u10dd\u10e5\u10e1\u10d8"},"ro":{"language":"ro","value":"Dumbarton Oaks"},"ar":{"language":"ar","value":"\u062f\u0648\u0645\u0628\u0627\u0631\u062a\u0648\u0646 \u0623\u0648\u0643\u0633"}},"descriptions":{"da":{"language":"da","value":"bygning i Georgetown i Washington D.C. Forskningsinsitution vedr\u00f8rende byzantinsk og pr\u00e6-columbiansk historie"},"en":{"language":"en","value":"research institution concerning Byzantine and Pre-Columbian studies"},"fr":{"language":"fr","value":"h\u00f4tel particulier avec jardin botanique, \u00e0 Washington"},"nl":{"language":"nl","value":"hortus botanicus in Washington D.C., Verenigde Staten van Amerika"},"ru":{"language":"ru","value":"\u0441\u0442\u0430\u0440\u0438\u043d\u043d\u044b\u0439 \u043e\u0441\u043e\u0431\u043d\u044f\u043a \u0432 \u0414\u0436\u043e\u0440\u0434\u0436\u0442\u0430\u0443\u043d\u0435"},"ar":{"language":"ar","value":"\u0645\u0624\u0633\u0633\u0629 \u0628\u062d\u062b\u064a\u0629 \u0645\u062e\u062a\u0635\u0629 \u0628\u0627\u0644\u062f\u0631\u0627\u0633\u0627\u062a \u0627\u0644\u0628\u064a\u0632\u0646\u0637\u064a\u0629 \u0648\u0645\u0627\u0642\u0628\u0644 \u0627\u0644\u0643\u0648\u0644\u0645\u0628\u064a\u0629"},"de":{"language":"de","value":"Landhaus des 19. Jahrhunderts in Washington, D.C."},"it":{"language":"it","value":"villa storica di Washington, D.C., ospita un istituto di studi bizantini, pre-colombiani e di architettura del paesaggio"}},"aliases":{"ru":[{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0435\u0440\u0442\u043e\u043d-\u041e\u043a\u0441"},{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0430\u0440\u0442\u043e\u043d-\u041e\u043a\u0441"},{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0430\u0440\u0442\u043e\u043d \u041e\u0430\u043a\u0441"},{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0430\u0440\u0442\u043e\u043d \u041e\u0443\u043a\u0441"},{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0430\u0440\u0442\u043e\u043d-\u041e\u0443\u043a\u0441"},{"language":"ru","value":"\u0414\u0430\u043c\u0431\u0435\u0440\u0442\u043e\u043d \u041e\u0430\u043a\u0441"}],"de":[{"language":"de","value":"Dumbarton Oaks Research Library and Collection"}],"tr":[{"language":"tr","value":"Dumbarton oaks"}],"es":[{"language":"es","value":"Instituto de Dumbarton Oaks"}],"en":[{"language":"en","value":"Dumbarton Oaks Research Library and Collection"}]},"claims":{"P1612":[{"mainsnak":{"snaktype":"value","property":"P1612","datavalue":{"value":"Dumbarton Oaks, Washington","type":"string"},"datatype":"string"},"type":"statement","id":"Q1264942$34c83e8d-4c4f-d18e-af09-fc1d053193e9","rank":"normal"}],"P373":[{"mainsnak":{"snaktype":"value","property":"P373","datavalue":{"value":"Dumbarton Oaks","type":"string"},"datatype":"string"},"type":"statement","id":"q1264942$E8B32884-59AB-437C-AD03-82F103AEFBE9","rank":"normal","references":[{"hash":"c456dc5cd2117249948c288206ff3f8b1bf574f0","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":8449,"id":"Q8449"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]}],"P625":[{"mainsnak":{"snaktype":"value","property":"P625","datavalue":{"value":{"latitude":38.91416666666667,"longitude":-77.06333333333333,"altitude":null,"precision":0.0002777777777777778,"globe":"http://www.wikidata.org/entity/Q2"},"type":"globecoordinate"},"datatype":"globe-coordinate"},"type":"statement","id":"q1264942$73CF12FA-19F7-4CA6-8688-D700FD682483","rank":"normal","references":[{"hash":"fa278ebfc458360e5aed63d5058cca83c46134f1","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":328,"id":"Q328"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]}],"P17":[{"mainsnak":{"snaktype":"value","property":"P17","datavalue":{"value":{"entity-type":"item","numeric-id":30,"id":"Q30"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"q1264942$6FF216BD-5CCB-4536-9416-C52EB01E7CB0","rank":"normal","references":[{"hash":"fa278ebfc458360e5aed63d5058cca83c46134f1","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":328,"id":"Q328"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]},{"hash":"66e668129940a484226929f8951581f03c2eae5d","snaks":{"P5383":[{"snaktype":"value","property":"P5383","datavalue":{"value":"7789","type":"string"},"datatype":"external-id"}],"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":265049,"id":"Q265049"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2018-07-31T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P5383","P248","P813"]}]}],"P131":[{"mainsnak":{"snaktype":"value","property":"P131","datavalue":{"value":{"entity-type":"item","numeric-id":61,"id":"Q61"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"q1264942$8D04F936-04A1-4769-B2E1-0F40B41661EB","rank":"normal","references":[{"hash":"fa278ebfc458360e5aed63d5058cca83c46134f1","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":328,"id":"Q328"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]},{"hash":"66e668129940a484226929f8951581f03c2eae5d","snaks":{"P5383":[{"snaktype":"value","property":"P5383","datavalue":{"value":"7789","type":"string"},"datatype":"external-id"}],"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":265049,"id":"Q265049"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2018-07-31T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P5383","P248","P813"]}]}],"P31":[{"mainsnak":{"snaktype":"value","property":"P31","datavalue":{"value":{"entity-type":"item","numeric-id":167346,"id":"Q167346"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$4049CD73-9ABB-4493-B085-6BD831124D69","rank":"normal","references":[{"hash":"fa278ebfc458360e5aed63d5058cca83c46134f1","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":328,"id":"Q328"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]},{"mainsnak":{"snaktype":"value","property":"P31","datavalue":{"value":{"entity-type":"item","numeric-id":1802963,"id":"Q1802963"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$a79bc8a1-4dde-7b87-9cb8-8fdcd61cd002","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","datavalue":{"value":{"entity-type":"item","numeric-id":1438040,"id":"Q1438040"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$b3179fc5-4b39-77bc-4e18-84f63fd6faf5","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","datavalue":{"value":{"entity-type":"item","numeric-id":31855,"id":"Q31855"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$5ef496fa-41e9-03ee-ab7f-40492cb2a08f","rank":"normal"}],"P646":[{"mainsnak":{"snaktype":"value","property":"P646","datavalue":{"value":"/m/03q_0d","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$36DF9487-9217-4956-B28D-F6FFC9DB806B","rank":"normal","references":[{"hash":"2b00cb481cddcac7623114367489b5c194901c4a","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":15241312,"id":"Q15241312"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P577":[{"snaktype":"value","property":"P577","datavalue":{"value":{"time":"+2013-10-28T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P577"]}]}],"P214":[{"mainsnak":{"snaktype":"value","property":"P214","datavalue":{"value":"126370132","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$914EE61C-AA96-4D7C-9753-3C1EDFDC2043","rank":"normal","references":[{"hash":"07bf3e9a3426ab5ce3b2354ef5f1a265064a8dad","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":54919,"id":"Q54919"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2015-08-09T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P813"]}]}],"P276":[{"mainsnak":{"snaktype":"value","property":"P276","datavalue":{"value":{"entity-type":"item","numeric-id":1468648,"id":"Q1468648"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$0a9b080c-4afa-fc37-b943-14eeff580fa8","rank":"normal"}],"P18":[{"mainsnak":{"snaktype":"value","property":"P18","datavalue":{"value":"Dumbarton Oaks - house photo with snow.jpg","type":"string"},"datatype":"commonsMedia"},"type":"statement","id":"Q1264942$3EA08B94-A420-47DB-BBB0-C03FE53AB7FB","rank":"normal","references":[{"hash":"14d2400e3b1d36332748dc330276f099eeaa8800","snaks":{"P143":[{"snaktype":"value","property":"P143","datavalue":{"value":{"entity-type":"item","numeric-id":1551807,"id":"Q1551807"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]}],"P213":[{"mainsnak":{"snaktype":"value","property":"P213","datavalue":{"value":"0000 0001 2108 6569","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$12ffbd75-42ef-a04d-ce18-837cfdbb8f8d","rank":"normal"}],"P244":[{"mainsnak":{"snaktype":"value","property":"P244","datavalue":{"value":"n80061150","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$48d0f5a6-4b51-0025-6284-5143a7be8cf4","rank":"normal"}],"P1670":[{"mainsnak":{"snaktype":"value","property":"P1670","datavalue":{"value":"0058K5557","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$7f2c9427-4df4-490d-ad02-918c88552abc","rank":"normal"}],"P1017":[{"mainsnak":{"snaktype":"value","property":"P1017","datavalue":{"value":"ADV10090087","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$f98c7030-477c-196d-02bf-a4b86ba69d7c","rank":"normal"}],"P571":[{"mainsnak":{"snaktype":"value","property":"P571","datavalue":{"value":{"time":"+1940-00-00T00:00:00Z","timezone":0,"before":0,"after":0,"precision":9,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"},"type":"statement","id":"Q1264942$29c190f0-4a58-c7d1-6cf9-bc51f0383d04","rank":"normal"}],"P3222":[{"mainsnak":{"snaktype":"value","property":"P3222","datavalue":{"value":"dumbarton-oaks-research-library-and-collection","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$C1374BD3-A7D8-45D6-9C86-6E47731BDA4C","rank":"normal"}],"P2581":[{"mainsnak":{"snaktype":"value","property":"P2581","datavalue":{"value":"03824328n","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$3350284F-5750-488C-83D0-41E46E8CC56B","rank":"normal","references":[{"hash":"248ac337a217a5f5eed7139a82a4e60931611af0","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":4837690,"id":"Q4837690"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P248"]}]}],"P3153":[{"mainsnak":{"snaktype":"value","property":"P3153","datavalue":{"value":"100005724","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$D3E36840-8E3A-428D-A7C5-4095EC98188B","rank":"normal","references":[{"hash":"c14b8135fd12fb3164536c79d76c114c1cff1909","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":19822542,"id":"Q19822542"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2017-12-07T00:00:00Z","timezone":0,"before":1,"after":1,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P813"]}]}],"P1417":[{"mainsnak":{"snaktype":"value","property":"P1417","datavalue":{"value":"place/Dumbarton-Oaks","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$AFC3A786-0A78-4144-80FD-0E1C47962C61","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P1417","datavalue":{"value":"topic/Dumbarton-Oaks-Research-Library-and-Collection","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$B4D9CDEB-6E60-456E-9955-80BE4859FFFE","rank":"normal"}],"P2163":[{"mainsnak":{"snaktype":"value","property":"P2163","datavalue":{"value":"544573","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$e2597e27-4303-2c4e-6205-1d9de134513b","rank":"normal"}],"P3430":[{"mainsnak":{"snaktype":"value","property":"P3430","datavalue":{"value":"w6sf71t8","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$4c0f9bb8-48dc-c694-e87a-b965bffa979a","rank":"normal"}],"P5361":[{"mainsnak":{"snaktype":"value","property":"P5361","datavalue":{"value":"DumbartonOaks","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$DBF7064C-7454-4908-BEA3-3D6308A71706","rank":"normal"}],"P5383":[{"mainsnak":{"snaktype":"value","property":"P5383","datavalue":{"value":"7789","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$C19489F5-0ECD-44C2-9305-4936EADBF7A8","rank":"normal","references":[{"hash":"fc5c0700e4ea82f652ee81db5553c3a44446a105","snaks":{"P854":[{"snaktype":"value","property":"P854","datavalue":{"value":"https://www.archinform.net/service/wd_aipro.php","type":"string"},"datatype":"url"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2018-07-02T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P854","P813"]}]}],"P268":[{"mainsnak":{"snaktype":"value","property":"P268","datavalue":{"value":"12126836p","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$123FF3BC-682F-4010-85A5-425696D16A64","rank":"normal","references":[{"hash":"5a858a70d6ac562123c1debf25a5aed93a5c4939","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":19938912,"id":"Q19938912"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2015-08-26T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P813"]}]}],"P269":[{"mainsnak":{"snaktype":"value","property":"P269","datavalue":{"value":"13632259X","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$C4106BFC-FA27-4518-B8CB-C0E93B1D5181","rank":"normal","references":[{"hash":"5a858a70d6ac562123c1debf25a5aed93a5c4939","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":19938912,"id":"Q19938912"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2015-08-26T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P813"]}]}],"P749":[{"mainsnak":{"snaktype":"value","property":"P749","datavalue":{"value":{"entity-type":"item","numeric-id":13371,"id":"Q13371"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$155bf2f2-4980-f469-2829-61e0a42d568c","rank":"normal"}],"P101":[{"mainsnak":{"snaktype":"value","property":"P101","datavalue":{"value":{"entity-type":"item","numeric-id":648154,"id":"Q648154"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$b876b9d8-4e74-b5d9-1927-bbb32aa835e6","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P101","datavalue":{"value":{"entity-type":"item","numeric-id":1964546,"id":"Q1964546"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$469b910a-469a-6df3-d8d2-7060e9890ded","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P101","datavalue":{"value":{"entity-type":"item","numeric-id":47844,"id":"Q47844"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$944ea844-41a2-d325-41e6-7a72213fe068","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P101","datavalue":{"value":{"entity-type":"item","numeric-id":202390,"id":"Q202390"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$d2e5da95-4178-f729-312f-0cbb3a95c13e","rank":"normal"}],"P3348":[{"mainsnak":{"snaktype":"value","property":"P3348","datavalue":{"value":"43992","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$dd26a0d0-47da-ec9b-3b10-f558dfd84ed0","rank":"normal"}],"P691":[{"mainsnak":{"snaktype":"value","property":"P691","datavalue":{"value":"kn20090113017","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$2DDD84BA-6506-42FB-B0AB-74FF194CC65F","rank":"normal","references":[{"hash":"2ea34d91e316dc345b07eda8fdc15de9dd505cfd","snaks":{"P214":[{"snaktype":"value","property":"P214","datavalue":{"value":"126370132","type":"string"},"datatype":"external-id"}],"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":54919,"id":"Q54919"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2018-12-17T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P214","P248","P813"]}]}],"P127":[{"mainsnak":{"snaktype":"value","property":"P127","datavalue":{"value":{"entity-type":"item","numeric-id":13371,"id":"Q13371"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","qualifiers":{"P580":[{"snaktype":"value","property":"P580","hash":"db280088b0cb6e154ee3aa6abd073619d5c712a0","datavalue":{"value":{"time":"+1940-00-00T00:00:00Z","timezone":0,"before":0,"after":0,"precision":9,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"qualifiers-order":["P580"],"id":"Q1264942$7457d01d-4605-8190-915b-56990704032b","rank":"normal"}],"P5818":[{"mainsnak":{"snaktype":"value","property":"P5818","datavalue":{"value":"4599","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$aa3dfbbb-4cfc-9944-cab8-173cb0dadde9","rank":"normal"}],"P4146":[{"mainsnak":{"snaktype":"value","property":"P4146","datavalue":{"value":"339","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$CFF9357F-676E-4357-9594-9A8883740ECA","rank":"normal"}],"P3500":[{"mainsnak":{"snaktype":"value","property":"P3500","datavalue":{"value":"8355","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$3E65F28F-F3C6-4C10-A15B-BBCB2B2384B1","rank":"normal","references":[{"hash":"6a3a3de3208a18f7470e4d83d4208b5ac2f57e20","snaks":{"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":64159407,"id":"Q64159407"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P356":[{"snaktype":"value","property":"P356","datavalue":{"value":"10.5281/ZENODO.758080","type":"string"},"datatype":"external-id"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2019-05-29T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P248","P356","P813"]}]}],"P7514":[{"mainsnak":{"snaktype":"value","property":"P7514","datavalue":{"value":{"entity-type":"item","numeric-id":437714,"id":"Q437714"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q1264942$e293895c-4089-92cb-d04d-f8a6bbdce239","rank":"normal"}],"P7859":[{"mainsnak":{"snaktype":"value","property":"P7859","datavalue":{"value":"lccn-n80061150","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$EEADBF38-DF92-45FF-9EF7-D90CC9DCC24E","rank":"normal","references":[{"hash":"fa9b3c964b02bdb40d4b735e5e8cea999edb7293","snaks":{"P214":[{"snaktype":"value","property":"P214","datavalue":{"value":"126370132","type":"string"},"datatype":"external-id"}]},"snaks-order":["P214"]}]}],"P3977":[{"mainsnak":{"snaktype":"value","property":"P3977","datavalue":{"value":"47839","type":"string"},"datatype":"external-id"},"type":"statement","qualifiers":{"P1810":[{"snaktype":"value","property":"P1810","hash":"207e62c8462fcebfe5d28fe799ff271a279cad57","datavalue":{"value":"Dumbarton Oaks","type":"string"},"datatype":"string"}]},"qualifiers-order":["P1810"],"id":"Q1264942$8e6806dd-431f-e6c3-7665-fa27a99408bc","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P3977","datavalue":{"value":"4081919","type":"string"},"datatype":"external-id"},"type":"statement","qualifiers":{"P1810":[{"snaktype":"value","property":"P1810","hash":"5b0af63eef2b4e3e8bc6acaa0de958e4c854ec16","datavalue":{"value":"Dumbarton Oaks Research Library & Collection","type":"string"},"datatype":"string"}]},"qualifiers-order":["P1810"],"id":"Q1264942$137def4b-4304-f68b-d26f-fc52647171fc","rank":"normal"}],"P8034":[{"mainsnak":{"snaktype":"value","property":"P8034","datavalue":{"value":"494/3605","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q1264942$8391F7EE-77CA-4EA7-88A6-8BDBD3985C4B","rank":"normal","references":[{"hash":"0e1dd32dd8684bd8397ff072ede359c6195d9375","snaks":{"P214":[{"snaktype":"value","property":"P214","datavalue":{"value":"126370132","type":"string"},"datatype":"external-id"}],"P248":[{"snaktype":"value","property":"P248","datavalue":{"value":{"entity-type":"item","numeric-id":54919,"id":"Q54919"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}],"P813":[{"snaktype":"value","property":"P813","datavalue":{"value":{"time":"+2020-07-16T00:00:00Z","timezone":0,"before":0,"after":0,"precision":11,"calendarmodel":"http://www.wikidata.org/entity/Q1985727"},"type":"time"},"datatype":"time"}]},"snaks-order":["P214","P248","P813"]}]}]},"sitelinks":{"arwiki":{"site":"arwiki","title":"\u062f\u0648\u0645\u0628\u0627\u0631\u062a\u0648\u0646 \u0623\u0648\u0643\u0633","badges":[],"url":"https://ar.wikipedia.org/wiki/%D8%AF%D9%88%D9%85%D8%A8%D8%A7%D8%B1%D8%AA%D9%88%D9%86_%D8%A3%D9%88%D9%83%D8%B3"},"astwiki":{"site":"astwiki","title":"Dumbarton Oaks","badges":[],"url":"https://ast.wikipedia.org/wiki/Dumbarton_Oaks"},"bewiki":{"site":"bewiki","title":"\u0414\u0443\u043c\u0431\u0430\u0440\u0442\u0430\u043d-\u041e\u043a\u0441","badges":[],"url":"https://be.wikipedia.org/wiki/%D0%94%D1%83%D0%BC%D0%B1%D0%B0%D1%80%D1%82%D0%B0%D0%BD-%D0%9E%D0%BA%D1%81"},"cawiki":{"site":"cawiki","title":"Dumbarton Oaks","badges":[],"url":"https://ca.wikipedia.org/wiki/Dumbarton_Oaks"},"commonswiki":{"site":"commonswiki","title":"Category:Dumbarton Oaks","badges":[],"url":"https://commons.wikimedia.org/wiki/Category:Dumbarton_Oaks"},"dewiki":{"site":"dewiki","title":"Dumbarton Oaks","badges":[],"url":"https://de.wikipedia.org/wiki/Dumbarton_Oaks"},"enwiki":{"site":"enwiki","title":"Dumbarton Oaks","badges":[],"url":"https://en.wikipedia.org/wiki/Dumbarton_Oaks"},"eswiki":{"site":"eswiki","title":"Dumbarton Oaks","badges":[],"url":"https://es.wikipedia.org/wiki/Dumbarton_Oaks"},"fawiki":{"site":"fawiki","title":"\u062f\u0627\u0645\u0628\u0631\u062a\u0648\u0646 \u0627\u06a9\u0633","badges":[],"url":"https://fa.wikipedia.org/wiki/%D8%AF%D8%A7%D9%85%D8%A8%D8%B1%D8%AA%D9%88%D9%86_%D8%A7%DA%A9%D8%B3"},"frwiki":{"site":"frwiki","title":"Dumbarton Oaks","badges":[],"url":"https://fr.wikipedia.org/wiki/Dumbarton_Oaks"},"hewiki":{"site":"hewiki","title":"\u05d3\u05de\u05d1\u05e8\u05d8\u05d5\u05df \u05d0\u05d5\u05e7\u05e1","badges":[],"url":"https://he.wikipedia.org/wiki/%D7%93%D7%9E%D7%91%D7%A8%D7%98%D7%95%D7%9F_%D7%90%D7%95%D7%A7%D7%A1"},"idwiki":{"site":"idwiki","title":"Dumbarton Oaks","badges":[],"url":"https://id.wikipedia.org/wiki/Dumbarton_Oaks"},"itwiki":{"site":"itwiki","title":"Dumbarton Oaks","badges":[],"url":"https://it.wikipedia.org/wiki/Dumbarton_Oaks"},"jawiki":{"site":"jawiki","title":"\u30c0\u30f3\u30d0\u30fc\u30c8\u30f3\u30fb\u30aa\u30fc\u30af\u30b9","badges":[],"url":"https://ja.wikipedia.org/wiki/%E3%83%80%E3%83%B3%E3%83%90%E3%83%BC%E3%83%88%E3%83%B3%E3%83%BB%E3%82%AA%E3%83%BC%E3%82%AF%E3%82%B9"},"plwiki":{"site":"plwiki","title":"Dumbarton Oaks","badges":[],"url":"https://pl.wikipedia.org/wiki/Dumbarton_Oaks"},"ptwiki":{"site":"ptwiki","title":"Dumbarton Oaks","badges":[],"url":"https://pt.wikipedia.org/wiki/Dumbarton_Oaks"},"rowiki":{"site":"rowiki","title":"Dumbarton Oaks","badges":[],"url":"https://ro.wikipedia.org/wiki/Dumbarton_Oaks"},"ruwiki":{"site":"ruwiki","title":"\u0414\u0443\u043c\u0431\u0430\u0440\u0442\u043e\u043d-\u041e\u043a\u0441","badges":[],"url":"https://ru.wikipedia.org/wiki/%D0%94%D1%83%D0%BC%D0%B1%D0%B0%D1%80%D1%82%D0%BE%D0%BD-%D0%9E%D0%BA%D1%81"},"srwiki":{"site":"srwiki","title":"\u0414\u0430\u043c\u0431\u0435\u0440\u0442\u043e\u043d \u041e\u0443\u043a\u0441","badges":[],"url":"https://sr.wikipedia.org/wiki/%D0%94%D0%B0%D0%BC%D0%B1%D0%B5%D1%80%D1%82%D0%BE%D0%BD_%D0%9E%D1%83%D0%BA%D1%81"},"trwiki":{"site":"trwiki","title":"Dumbarton Oaks","badges":[],"url":"https://tr.wikipedia.org/wiki/Dumbarton_Oaks"},"zhwiki":{"site":"zhwiki","title":"\u6566\u5df4\u9813\u6a61\u6a39\u5712","badges":[],"url":"https://zh.wikipedia.org/wiki/%E6%95%A6%E5%B7%B4%E9%A0%93%E6%A9%A1%E6%A8%B9%E5%9C%92"}}}}} \ No newline at end of file diff --git a/jest.config.mjs b/jest.config.mjs index 2dc92fe..e088894 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -18,6 +18,7 @@ export default { roots: ['/src', '/test'], moduleNameMapper: { '^SolidLogic$': 'solid-logic', - '^\\$rdf$': 'rdflib' + '^\\$rdf$': 'rdflib', + '\\.css$': '/test/__mocks__/styleMock.js' }, } diff --git a/package-lock.json b/package-lock.json index 22ae04b..9f4ace2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,13 @@ { "name": "contacts-pane", - "version": "3.0.1", + "version": "3.1.0-newStyle", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "contacts-pane", - "version": "3.0.1", + "version": "3.1.0-newStyle", "license": "MIT", - "dependencies": { - "mime-types": "^3.0.2" - }, "devDependencies": { "@babel/cli": "^7.28.6", "@babel/core": "^7.28.6", @@ -22,25 +19,35 @@ "babel-jest": "^30.2.0", "babel-loader": "^10.0.0", "babel-plugin-inline-import": "^3.0.0", + "baseline-browser-mapping": "^2.9.19", "buffer": "^6.0.3", + "copy-webpack-plugin": "^13.0.1", + "css-loader": "^7.1.3", "eslint": "^9.39.2", "eslint-plugin-import": "^2.32.0", "globals": "^17.1.0", + "html-webpack-plugin": "^5.6.6", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "jest-fetch-mock": "^3.0.3", "neostandard": "^0.12.2", + "node-polyfill-webpack-plugin": "^4.1.0", "pane-registry": "^3.0.1", "rdflib": "^2.3.5", "solid-logic": "^4.0.2", - "solid-ui": "^3.0.3", + "solid-ui": "3.0.4-822a480", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.16", "typescript": "^5.9.3", - "typescript-transpile-only": "^0.0.4" + "typescript-transpile-only": "^0.0.4", + "webpack": "^5.0.0", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.3" }, "peerDependencies": { "rdflib": "^2.3.5", "solid-logic": "^4.0.2", - "solid-ui": "^3.0.3" + "solid-ui": "3.0.4-822a480" } }, "node_modules/@adobe/css-tools": { @@ -2024,6 +2031,16 @@ "node": ">=18.0" } }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, "node_modules/@emnapi/core": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", @@ -3114,7 +3131,6 @@ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3138,6 +3154,13 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -3198,6 +3221,242 @@ "node": ">=12.4.0" } }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", + "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-cms/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", + "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", + "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", + "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-rsa": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", + "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", + "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pfx": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", + "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", + "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", + "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", + "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@peculiar/x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3383,13 +3642,54 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3401,7 +3701,6 @@ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -3414,6 +3713,56 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -3478,6 +3827,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -3488,6 +3844,80 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3502,6 +3932,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -4175,7 +4615,6 @@ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -4186,24 +4625,21 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", @@ -4211,7 +4647,6 @@ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4223,8 +4658,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", @@ -4232,7 +4666,6 @@ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4246,7 +4679,6 @@ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -4257,7 +4689,6 @@ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -4267,8 +4698,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", @@ -4276,7 +4706,6 @@ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4294,7 +4723,6 @@ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -4309,7 +4737,6 @@ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4323,7 +4750,6 @@ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4339,37 +4765,81 @@ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -4384,6 +4854,53 @@ "node": ">=6.5" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -4403,7 +4920,6 @@ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -4454,7 +4970,6 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -4473,7 +4988,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4490,8 +5004,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ansi-escapes": { "version": "4.3.2", @@ -4509,6 +5022,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4586,6 +5112,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, "node_modules/array-includes": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", @@ -4729,6 +5262,61 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/asn1js": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", + "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/asn1js/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -4971,13 +5559,19 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=8" }, @@ -4985,6 +5579,86 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -5008,6 +5682,156 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.17.0" + } + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz", + "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", + "dev": true, + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.2", + "browserify-rsa": "^4.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.6.1", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.9", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -5084,32 +5908,82 @@ "dev": true, "license": "MIT" }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "run-applescript": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", - "dependencies": { + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" }, @@ -5144,6 +6018,24 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camel-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -5155,15 +6047,9 @@ } }, "node_modules/caniuse-lite": { -<<<<<<< dependabot/npm_and_yarn/npm_and_yarn-c081eec248 "version": "1.0.30001769", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", -======= - "version": "1.0.30001767", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", - "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", ->>>>>>> main "dev": true, "funding": [ { @@ -5224,7 +6110,6 @@ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5250,7 +6135,6 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -5271,6 +6155,21 @@ "node": ">=8" } }, + "node_modules/cipher-base": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cjs-module-lexer": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", @@ -5278,6 +6177,19 @@ "dev": true, "license": "MIT" }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -5293,6 +6205,21 @@ "node": ">=12" } }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -5331,6 +6258,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -5351,6 +6285,55 @@ "node": ">= 12.0.0" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5358,6 +6341,52 @@ "dev": true, "license": "MIT" }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5365,6 +6394,70 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-webpack-plugin": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", + "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-parent": "^6.0.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2", + "tinyglobby": "^0.2.12" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/core-js": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", @@ -5391,6 +6484,67 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -5416,6 +6570,33 @@ "node": ">= 8" } }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", @@ -5423,44 +6604,136 @@ "dev": true, "license": "MIT" }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", - "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "node_modules/css-loader": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.3.tgz", + "integrity": "sha512-frbERmjT0UC5lMheWpJmMilnt9GEhbZJN/heUb7/zaJYeIzj5St9HvDcfshzzOqbsS+rYpMk++2SD3vGETDSyA==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" }, "engines": { - "node": ">=18" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "node_modules/css-loader/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", @@ -5569,6 +6842,36 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5587,6 +6890,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -5605,6 +6921,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5615,6 +6963,45 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5635,6 +7022,116 @@ "dev": true, "license": "MIT" }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domain-browser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", + "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5657,19 +7154,43 @@ "dev": true, "license": "MIT" }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { -<<<<<<< dependabot/npm_and_yarn/npm_and_yarn-c081eec248 "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", -======= - "version": "1.5.283", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", - "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==", ->>>>>>> main "dev": true, "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -5690,6 +7211,16 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.19.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", @@ -5717,6 +7248,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -5849,8 +7393,7 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -6573,6 +8116,16 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -6583,6 +8136,13 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -6593,6 +8153,17 @@ "node": ">=0.8.x" } }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6645,15 +8216,79 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, @@ -6681,8 +8316,30 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } }, "node_modules/fb-watchman": { "version": "2.0.2", @@ -6720,6 +8377,42 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -6737,6 +8430,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -6758,6 +8461,27 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -6804,6 +8528,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -7025,7 +8769,6 @@ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", - "optional": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -7038,8 +8781,7 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.12", @@ -7129,6 +8871,13 @@ "dev": true, "license": "MIT" }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -7210,6 +8959,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7223,6 +8997,81 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -7243,6 +9092,151 @@ "dev": true, "license": "MIT" }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", + "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -7257,6 +9251,38 @@ "node": ">= 14" } }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true, + "license": "MIT" + }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -7281,6 +9307,16 @@ "node": ">=10.17.0" } }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -7294,6 +9330,19 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -7426,7 +9475,44 @@ "node": ">= 0.4" } }, - "node_modules/is-array-buffer": { + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", @@ -7493,7 +9579,6 @@ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -7605,6 +9690,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -7684,6 +9785,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -7697,6 +9817,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -7710,6 +9847,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7737,6 +9887,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -7902,6 +10078,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -7916,6 +10108,26 @@ "dev": true, "license": "ISC" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isomorphic-timers-promises": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", + "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -8984,6 +11196,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ky": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.1.tgz", @@ -8997,6 +11219,17 @@ "url": "https://github.com/sindresorhus/ky?sponsor=1" } }, + "node_modules/launch-editor": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", + "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9034,7 +11267,6 @@ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" }, @@ -9059,6 +11291,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -9086,6 +11325,23 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lower-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -9140,115 +11396,739 @@ "node": ">= 0.4" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", + "node_modules/memfs": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.56.10.tgz", + "integrity": "sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "mime-db": "^1.54.0" + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-fsa": "4.56.10", + "@jsonjoy.com/fs-node": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-to-fsa": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/fs-print": "4.56.10", + "@jsonjoy.com/fs-snapshot": "4.56.10", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=10.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/memfs/node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=6" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/memfs/node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=4" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/memfs/node_modules/@jsonjoy.com/fs-core": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.56.10.tgz", + "integrity": "sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "thingies": "^2.5.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/memfs/node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.56.10.tgz", + "integrity": "sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.56.10.tgz", + "integrity": "sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/fs-print": "4.56.10", + "@jsonjoy.com/fs-snapshot": "4.56.10", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.56.10.tgz", + "integrity": "sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.56.10.tgz", + "integrity": "sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.56.10", + "@jsonjoy.com/fs-node-builtins": "4.56.10", + "@jsonjoy.com/fs-node-utils": "4.56.10" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.56.10.tgz", + "integrity": "sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.56.10" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-print": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.56.10.tgz", + "integrity": "sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.56.10", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.56.10", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.56.10.tgz", + "integrity": "sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.56.10", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/thingies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", + "integrity": "sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/memfs/node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/memfs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, "node_modules/n3": { "version": "1.26.0", "resolved": "https://registry.npmjs.org/n3/-/n3-1.26.0.tgz", @@ -9263,6 +12143,25 @@ "node": ">=12.0" } }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -9286,13 +12185,22 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/neostandard": { "version": "0.12.2", @@ -9336,6 +12244,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/no-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9371,30 +12297,159 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/node-fetch/node_modules/whatwg-url": { + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-polyfill-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-b4ei444EKkOagG/yFqojrD3QTYM5IOU1f8tn9o6uwrG4qL+brI7oVhjPVd0ZL2xy+Z6CP5bu9w8XTvlWgiXHcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-stdlib-browser": "^1.3.0", + "type-fest": "^4.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "webpack": ">=5" + } + }, + "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-stdlib-browser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz", + "integrity": "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert": "^2.0.0", + "browser-resolve": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "create-require": "^1.1.1", + "crypto-browserify": "^3.12.1", + "domain-browser": "4.22.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "isomorphic-timers-promises": "^1.0.1", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "pkg-dir": "^5.0.0", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.1", + "url": "^0.11.4", + "util": "^0.12.4", + "vm-browserify": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/node-stdlib-browser/node_modules/pkg-dir": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "node_modules/node-stdlib-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true, "license": "MIT" }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "node_modules/node-stdlib-browser/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -9419,6 +12474,19 @@ "node": ">=8" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nwsapi": { "version": "2.2.22", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", @@ -9449,6 +12517,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -9549,6 +12634,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9575,6 +12690,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -9593,6 +12727,13 @@ "node": ">= 0.8.0" } }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true, + "license": "MIT" + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -9643,6 +12784,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -9660,6 +12819,13 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/pane-registry": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pane-registry/-/pane-registry-3.0.1.tgz", @@ -9671,6 +12837,24 @@ "solid-logic": "^4.0.2" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9684,6 +12868,23 @@ "node": ">=6" } }, + "node_modules/parse-asn1": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "pbkdf2": "^3.1.5", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9716,6 +12917,41 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9784,6 +13020,31 @@ "dev": true, "license": "ISC" }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pbkdf2": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "ripemd160": "^2.0.3", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/peowly": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/peowly/-/peowly-1.3.2.tgz", @@ -9821,97 +13082,248 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkijs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz", + "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/pkijs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": ">= 6" + "node": "^10 || ^12 || >=14" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, + "license": "ISC", "engines": { - "node": ">=8" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-locate": "^4.1.0" + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-try": "^2.0.0" + "icss-utils": "^5.0.0" }, "engines": { - "node": ">=6" + "node": "^10 || ^12 || >= 14" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } + "license": "MIT" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -9923,6 +13335,17 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, "node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -9961,6 +13384,13 @@ "node": ">= 0.6.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, "node_modules/promise-polyfill": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", @@ -9987,6 +13417,52 @@ "dev": true, "license": "MIT" }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -10014,6 +13490,58 @@ ], "license": "MIT" }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvtsutils/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10024,6 +13552,56 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rdf-canonize": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-5.0.0.tgz", @@ -10094,7 +13672,6 @@ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -10102,6 +13679,19 @@ "node": ">=8.10.0" } }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -10116,6 +13706,13 @@ "node": ">=8" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -10218,6 +13815,30 @@ "regjsparser": "bin/parser" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10234,7 +13855,6 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -10249,6 +13869,13 @@ "x-path": "^0.0.2" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -10277,32 +13904,119 @@ "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ripemd160": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/ripemd160/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ripemd160/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/ripemd160/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/ripemd160/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "dependencies": { + "safe-buffer": "~5.1.0" } }, + "node_modules/ripemd160/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/rrweb-cssom": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", @@ -10310,6 +14024,19 @@ "dev": true, "license": "MIT" }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -10412,7 +14139,6 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -10433,7 +14159,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -10451,7 +14176,6 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -10464,8 +14188,28 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" + }, + "engines": { + "node": ">=18" + } }, "node_modules/semver": { "version": "6.3.1", @@ -10477,6 +14221,48 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/serialize-javascript": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", @@ -10487,6 +14273,122 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -10543,6 +14445,47 @@ "dev": true, "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10566,6 +14509,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -10659,6 +14615,28 @@ "node": ">=6" } }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/solid-logic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-4.0.2.tgz", @@ -10684,9 +14662,9 @@ "license": "MIT" }, "node_modules/solid-ui": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-3.0.4.tgz", - "integrity": "sha512-WJt4LT+GC61ai85RFlv+fduLSTksd2CwqLUVQy+7vU2+CzybxbT/nn2pwLOVOoIWPXTN8kW9ZL1mVnxnr2ETtQ==", + "version": "3.0.4-822a480", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-3.0.4-822a480.tgz", + "integrity": "sha512-wzk7PmJovmOfOvrNeGt4FxkdXJnoWVMdpljEJt8iNbIJ8p1wDctDI91SMfkYdB6tfuO/RGm7fTURzR+jBXtH7w==", "dev": true, "license": "MIT", "dependencies": { @@ -10730,6 +14708,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -10741,6 +14729,53 @@ "source-map": "^0.6.0" } }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10784,22 +14819,86 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 6" } }, "node_modules/string_decoder": { @@ -11028,6 +15127,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11097,7 +15213,6 @@ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -11117,7 +15232,6 @@ "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -11153,7 +15267,6 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -11169,7 +15282,6 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -11180,7 +15292,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11196,8 +15307,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", @@ -11205,7 +15315,6 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -11250,6 +15359,26 @@ "node": "*" } }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -11325,6 +15454,21 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11338,6 +15482,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -11456,6 +15610,26 @@ "dev": true, "license": "0BSD" }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true, + "license": "MIT" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -11492,6 +15666,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -11729,6 +15940,16 @@ "node": ">=4" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -11805,6 +16026,65 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -11834,6 +16114,23 @@ "node": ">=10.12.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -11863,7 +16160,6 @@ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -11872,6 +16168,16 @@ "node": ">=10.13.0" } }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -11888,7 +16194,6 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -11932,13 +16237,168 @@ } } }, + "node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", + "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.25", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.8.1", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.22.1", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^5.5.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/webpack-sources": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" } @@ -11949,7 +16409,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -11964,7 +16423,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -11975,7 +16433,6 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -11986,7 +16443,6 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -11994,6 +16450,31 @@ "node": ">= 0.6" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -12136,6 +16617,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -12239,6 +16727,22 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/x-path": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/x-path/-/x-path-0.0.2.tgz", @@ -12266,6 +16770,16 @@ "dev": true, "license": "MIT" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 4485eec..ebe0700 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "contacts-pane", - "version": "3.0.1", + "version": "3.1.0-newStyle", "description": "Contacts Pane: Contacts manager for Address Book, Groups, and Individuals.", "main": "dist/contactsPane.js", "files": [ @@ -10,19 +10,17 @@ ], "scripts": { "clean": "rm -rf dist", - "build": "npm run clean && npm run typecheck && npm run build-dist", - "build-dist": "npm run build-js && npm run build-types", - "build-js": "babel src --out-dir dist --extensions '.ts,.js' --source-maps", - "build-types": "tsc --emitDeclarationOnly", + "build": "npm run clean && npm run build-dist", + "build-dist": "webpack --progress", "lint": "eslint", "lint-fix": "eslint --fix", - "typecheck": "tsc --noEmit", "typecheck-test": "tsc --noEmit -p tsconfig.test.json", "test": "jest --no-coverage", "test-coverage": "jest --coverage", "prepublishOnly": "npm run build && npm run lint && npm run test", - "preversion": "npm run lint && npm run typecheck && npm run test", - "postpublish": "git push origin main --follow-tags" + "preversion": "npm run lint && npm run test", + "postpublish": "git push origin main --follow-tags", + "start": "webpack serve --config webpack.dev.config.mjs --open" }, "repository": { "type": "git", @@ -47,13 +45,10 @@ "url": "https://github.com/SolidOS/contacts-pane/issues" }, "homepage": "https://github.com/SolidOS/contacts-pane", - "dependencies": { - "mime-types": "^3.0.2" - }, "peerDependencies": { "rdflib": "^2.3.5", "solid-logic": "^4.0.2", - "solid-ui": "^3.0.3" + "solid-ui": "3.0.4-822a480" }, "devDependencies": { "@babel/cli": "^7.28.6", @@ -66,19 +61,29 @@ "babel-jest": "^30.2.0", "babel-loader": "^10.0.0", "babel-plugin-inline-import": "^3.0.0", + "baseline-browser-mapping": "^2.9.19", "buffer": "^6.0.3", + "copy-webpack-plugin": "^13.0.1", + "css-loader": "^7.1.3", "eslint": "^9.39.2", "eslint-plugin-import": "^2.32.0", "globals": "^17.1.0", + "html-webpack-plugin": "^5.6.6", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", "jest-fetch-mock": "^3.0.3", "neostandard": "^0.12.2", + "node-polyfill-webpack-plugin": "^4.1.0", "pane-registry": "^3.0.1", "rdflib": "^2.3.5", "solid-logic": "^4.0.2", - "solid-ui": "^3.0.3", + "solid-ui": "3.0.4-822a480", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.16", "typescript": "^5.9.3", - "typescript-transpile-only": "^0.0.4" + "typescript-transpile-only": "^0.0.4", + "webpack": "^5.0.0", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.3" } } diff --git a/src/addressBookPresenter.js b/src/addressBookPresenter.js new file mode 100644 index 0000000..758ca70 --- /dev/null +++ b/src/addressBookPresenter.js @@ -0,0 +1,505 @@ +import { addPersonToGroup, groupMembers, getDataModelIssues } from './contactLogic' +import * as UI from 'solid-ui' +import { store } from 'solid-logic' +import * as debug from './debug' +import { complain, complainIfBad, getSameAs, deleteRecursive, deleteThingAndDoc, compareForSort, nameFor } from './localUtils' +import { groupMembership } from './groupMembershipControl' + +const ns = UI.ns +const utils = UI.utils +const kb = store +let dom +let selectedGroups = {} +let selectedPeople = {} +let ulPeople = null +let ulGroups = null +let searchInput = null +let cardMain = null +let book = null +let div = null +let dataBrowserContext = null +let onGroupButtonClick = null + +// ######## Group presenter + +export function setActiveGroupButton (groupsUl, activeBtn) { + groupsUl.querySelectorAll('button').forEach(btn => { + btn.classList.remove('btn-primary', 'allGroupsButton--selected', 'allGroupsButton--active', 'allGroupsButton--loaded') + btn.classList.add('btn-secondary') + }) + if (activeBtn) { + activeBtn.classList.remove('btn-secondary') + activeBtn.classList.add('btn-primary') + } +} + +export function renderGroupButtons (currentBook, groupsUl, options, domElement, groupsSelected, peopleUl, searchEl, cardMainEl, divEl, context, groupClickCallback) { + dom = domElement + selectedGroups = groupsSelected || {} + if (peopleUl) ulPeople = peopleUl + if (searchEl) searchInput = searchEl + if (cardMainEl) cardMain = cardMainEl + if (divEl) div = divEl + if (context) dataBrowserContext = context + if (groupClickCallback) onGroupButtonClick = groupClickCallback + book = currentBook + ulGroups = groupsUl + const groups = groupsInOrder(book, options) + utils.syncTableToArrayReOrdered(ulGroups, groups, renderGroupLi) +} + +/** Create the common DOM structure for a group list item (li + button). + * Returns { groupLi, groupButton, name } so callers can attach their own handlers. + */ +export function createGroupLi (group) { + const name = kb.any(group, ns.vcard('fn')) + const groupLi = dom.createElement('li') + groupLi.setAttribute('role', 'listitem') + groupLi.setAttribute('aria-label', name ? name.value : 'Some group') + groupLi.subject = group + UI.widgets.makeDraggable(groupLi, group) + + const groupButton = groupLi.appendChild(dom.createElement('button')) + groupButton.setAttribute('type', 'button') + groupButton.innerHTML = name ? name.value : 'Some group' + groupButton.classList.add('allGroupsButton', 'actionButton', 'btn-secondary', 'action-button-focus') + + return { groupLi, groupButton, name } +} + +function renderGroupLi (group) { + async function handleURIsDroppedOnGroup (uris) { + for (const u of uris) { + debug.log('Dropped on group: ' + u) + const thing = kb.sym(u) + try { + await addPersonToGroup(thing, group) + } catch (e) { + complain(e) + } + refreshNames(ulPeople) + } + } + function groupLiClickListener (event) { + event.preventDefault() + setActiveGroupButton(ulGroups, groupButton) + if (onGroupButtonClick) onGroupButtonClick() + if (!event.metaKey) { + for (const key in selectedGroups) delete selectedGroups[key] // If Command key pressed, accumulate multiple + } + selectedGroups[group.uri] = !selectedGroups[group.uri] + refreshThingsSelected(ulGroups, selectedGroups) + // Load group members and refresh people list + kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (ok, _message) { + if (ok) { + refreshNames(ulPeople, null, false) + } + }) + } + + const { groupLi, groupButton } = createGroupLi(group) + + groupButton.addEventListener('click', groupLiClickListener, false) + UI.widgets.makeDropTarget(groupLi, handleURIsDroppedOnGroup) + groupLi.addEventListener('click', groupLiClickListener, true) + return groupLi +} // renderGroupLi + +export function selectAllGroups ( + selectedGroups, + ulGroups, + callbackFunction +) { + function fetchGroupAndSelect (group, groupLi) { + groupLi.classList.add('group-loading') + groupLi.setAttribute('aria-busy', 'true') + kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function ( + ok, + message + ) { + if (!ok) { + const msg = 'Can\'t load group file: ' + group + ': ' + message + badness.push(msg) + groupLi.setAttribute('aria-busy', 'false') + return complainIfBad(div, dom, ok, msg) + } + groupLi.classList.remove('group-loading') + groupLi.setAttribute('aria-busy', 'false') + groupLi.classList.add('selected') + selectedGroups[group.uri] = true + refreshThingsSelected(ulGroups, selectedGroups) + refreshNames(ulPeople, null) // @@ every time?? + todo -= 1 + if (!todo) { + if (callbackFunction) { callbackFunction(badness.length === 0, badness) } + } + }) + } + + const badness = [] + let todo = 0 + for (let k = 0; k < ulGroups.children.length; k++) { + const groupLi = ulGroups.children[k] + const group = groupLi.subject + if (!group) continue // Skip non-group items (e.g. All contacts, New group) + todo++ + fetchGroupAndSelect(group, groupLi) + } // for each row + if (todo === 0 && callbackFunction) { callbackFunction(true, badness) } +} + +export function refreshThingsSelected (ul, selectionArray) { + for (let i = 0; i < ul.children.length; i++) { + const li = ul.children[i] + if (li.subject) { + li.classList.toggle('selected', !!selectionArray[li.subject.uri]) + } + } +} + +export function syncGroupUl (book, options, groupsUl, domElement, groupsSelected, peopleUl, searchEl) { + dom = domElement + if (groupsSelected) selectedGroups = groupsSelected + if (peopleUl) ulPeople = peopleUl + if (searchEl) searchInput = searchEl + ulGroups = groupsUl + const groups = groupsInOrder(book, options) + if (groups.length > 0) { + renderGroupLi(groups[0]) // pre-render one to get the style right, then throw it away + } + utils.syncTableToArrayReOrdered(groupsUl, groups, renderGroupLi) + // refreshThingsSelected(groupsUl, selectedGroups) +} + +function groupsInOrder (book, options) { + let sortMe = [] + if (options.foreignGroup) { + sortMe.push([ + '', + kb.any(options.foreignGroup, ns.vcard('fn')), + options.foreignGroup + ]) + } + if (book) { + const groupIndex = kb.any(book, ns.vcard('groupIndex')) + const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : [] + const gs2 = gs.map(function (g) { + return [book, kb.any(g, ns.vcard('fn')), g] + }) + sortMe = sortMe.concat(gs2) + sortMe.sort() + } + return sortMe.map(tuple => tuple[2]) +} + +export async function loadAllGroups (book) { + const groupIndex = kb.any(book, ns.vcard('groupIndex')) + if (groupIndex) { + await kb.fetcher.load(groupIndex) + const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : [] + await kb.fetcher.load(gs) + return gs + } else return +} + +// The book could be the main subject, or linked from a group we are dealing with +export function findBookFromGroups (book) { + if (book) { + return book + } + let g + for (const gu in selectedGroups) { + g = kb.sym(gu) + const b = kb.any(undefined, ns.vcard('includesGroup'), g) + if (b) return b + } + throw new Error( + 'findBookFromGroups: Cant find address book which this group is part of' + ) +} +// ######## Group presenter - END + +// ######## Person presenter +/** Refresh the list of names */ +export function refreshNames (ulPeople, detailsView, autoSelect = true) { + function setPersonListener (personLi, person) { + function handleSelect (event) { + event.preventDefault() + selectPerson(ulPeople, person, cardMain) + } + personLi.addEventListener('click', handleSelect) + personLi.addEventListener('keydown', function (event) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + handleSelect(event) + } + }) + } + + let cards = [] + const groups = Object.keys(selectedGroups).map(groupURI => kb.sym(groupURI)) + groups.forEach(group => { + if (selectedGroups[group.value]) { + cards = cards.concat(groupMembers(kb, group)) + } + }) + cards.sort(compareForSort) // @@ sort by name not UID later + for (let k = 0; k < cards.length - 1;) { + if (cards[k].uri === cards[k + 1].uri) { + cards.splice(k, 1) // Eliminate duplicates from more than one group + } else { + k++ + } + } + + function renderNameInGroupList (person, ulPeople) { + const personLi = dom.createElement('li') + personLi.setAttribute('role', 'listitem') + personLi.setAttribute('tabindex', '0') + personLi.classList.add('personLi') + personLi.subject = person + UI.widgets.makeDraggable(personLi, person) + + // Container for the row + const rowDiv = dom.createElement('div') + rowDiv.classList.add('personLi-row') + + // Left: Avatar + const avatarDiv = dom.createElement('div') + avatarDiv.classList.add('personLi-avatar') + // Placeholder avatar (shown initially while person doc loads) + const placeholderEl = dom.createElement('div') + placeholderEl.classList.add('avatar-placeholder') + placeholderEl.innerHTML = '' + avatarDiv.appendChild(placeholderEl) + + // Get name early so it can be used in trySetAvatar + const name = nameFor(person) || 'Unknown Name' + + // Try to set avatar from already-loaded data, or fetch the person's doc + function trySetAvatar () { + const avatarUrl = kb.any(person, ns.vcard('hasPhoto')) + if (avatarUrl && avatarUrl.value) { + const img = dom.createElement('img') + img.src = avatarUrl.value + img.alt = name + ' avatar' + avatarDiv.replaceChild(img, avatarDiv.firstChild) + } + } + trySetAvatar() // check if already in store + // Load person's own document in background to get hasPhoto + kb.fetcher.nowOrWhenFetched(person.doc(), undefined, function (ok) { + if (ok) trySetAvatar() + }) + + // Center: Name + const infoDiv = dom.createElement('div') + infoDiv.classList.add('personLi-info') + + personLi.setAttribute('aria-label', name) + const nameDiv = dom.createElement('div') + nameDiv.classList.add('personLi-name') + nameDiv.textContent = name + + infoDiv.appendChild(nameDiv) + + // Right: Arrow icon + const arrowDiv = dom.createElement('div') + arrowDiv.classList.add('personLi-arrow') + arrowDiv.innerHTML = '' + + // Assemble + rowDiv.appendChild(avatarDiv) + rowDiv.appendChild(infoDiv) + rowDiv.appendChild(arrowDiv) + personLi.appendChild(rowDiv) + + setPersonListener(personLi, person) + return personLi + } + + utils.syncTableToArrayReOrdered(ulPeople, cards, person => renderNameInGroupList(person, ulPeople)) + refreshFilteredPeople(ulPeople, autoSelect, detailsView || cardMain) +} // refreshNames + +function selectPerson (ulPeople, person, detailsView) { + if (!detailsView) return + if (detailsView.parentNode) detailsView.parentNode.classList.remove('hidden') + detailsView.innerHTML = 'Loading...' + detailsView.setAttribute('aria-busy', 'true') + detailsView.classList.add('detailsSectionContent--wide') + selectedPeople = {} + selectedPeople[person.uri] = true + refreshFilteredPeople(ulPeople, false, detailsView) // Color to remember which one you picked + const local = book ? localNode(person) : person + kb.fetcher.nowOrWhenFetched(local.doc(), undefined, function ( + ok, + message + ) { + detailsView.innerHTML = '' + detailsView.setAttribute('aria-busy', 'false') + if (!ok) { + return complainIfBad(div, dom, ok, 'Can\'t load card: ' + local + ': ' + message) + } + // debug.log("Loaded card " + local + '\n') + + // Top-right toolbar with link icon and delete button + const toolbar = dom.createElement('div') + toolbar.classList.add('contact-toolbar') + const linkEl = UI.widgets.linkIcon(dom, local) + linkEl.setAttribute('title', 'Uri of contact') + toolbar.appendChild(linkEl) + + // Add in a delete button to delete from AB + const deleteButton = UI.widgets.deleteButtonWithCheck( + dom, + toolbar, // appends it to toolbar.appendChild(deleteButton) + 'contact', + async function () { + const container = person.dir() // ASSUMPTION THAT CARD IS IN ITS OWN DIRECTORY + + const pname = kb.any(person, ns.vcard('fn')) + debug.log('We are about to delete the contact ' + pname) + await loadAllGroups() // need to wait for all groups to be loaded in case they have a link to this person + // load people.ttl + let nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) + await kb.fetcher.load(nameEmailIndex) + + // - delete person's WebID's in each Group + // - delete the references to it in group files and save them back + // - delete the reference in people.ttl and save it back + + // find all Groups + const groups = groupMembership(person) + let removeFromGroups = [] + // find person WebID's + groups.forEach(group => { + const webids = getSameAs(kb, person, group.doc()) + // for each check in each Group that it is not used by an other person then delete + webids.forEach(webid => { + if (getSameAs(kb, webid, group.doc()).length === 1) { + removeFromGroups = removeFromGroups.concat(kb.statementsMatching(group, ns.vcard('hasMember'), webid, group.doc())) + } + }) + }) + + // Only if folder deletion succeeds, proceed with person deletion + await kb.updater.updateMany(removeFromGroups) + + try { + await deleteThingAndDoc(person) + } catch (err) { + debug.log('Contact deletion cancelled or failed: ' + err.message) + return // Stop - don't run deleteRecursive + } + + try { + await deleteRecursive(kb, container, toolbar, dom) + } catch (err) { + debug.log('Deletion cancelled: ' + err.message) + return // Exit without continuing with other deletions + } + complain(div, dom, "Contact data deleted.") + refreshNames(ulPeople, detailsView) + detailsView.innerHTML = 'Contact data deleted.' + } + ) + deleteButton.classList.add('deleteButton') + detailsView.appendChild(toolbar) + + detailsView.classList.add('detailsSectionContent--wide') + detailsView.appendChild(renderPane(local, 'contact')) + }) +} + +export function deselectAllPeople (ulPeople) { + selectedPeople = {} + if (ulPeople) { + for (let i = 0; i < ulPeople.children.length; i++) { + ulPeople.children[i].classList.remove('selected') + } + } +} + +export function refreshFilteredPeople (ulPeople, active, detailsView) { + let count = 0 + let lastRow = null + for (let i = 0; i < ulPeople.children.length; i++) { + const liElement = ulPeople.children[i] + const matches = filterName(nameFor(liElement.subject)) + if (matches) { + count++ + lastRow = liElement + } + liElement.classList.toggle('selected', matches && !!selectedPeople[liElement.subject.uri]) + liElement.classList.toggle('hidden', !matches) + } + if (count === 1 && active) { + const unique = lastRow.subject + selectPerson(ulPeople, unique, detailsView) + } +} + +function filterName (name) { + const filter = searchInput.value.trim().toLowerCase() + if (filter.length === 0) return true + const parts = filter.split(' ') // Each name part must be somewhere + for (let j = 0; j < parts.length; j++) { + const word = parts[j] + if (name.toLowerCase().indexOf(word) < 0) return false + } + return true +} + +function renderPane (subject, paneName) { + const p = dataBrowserContext.session.paneRegistry.byName(paneName) + const d = p.render(subject, dataBrowserContext) + d.classList.add('renderPane') + return d +} + +function localNode (person) { + const aliases = kb.allAliases(person) + const prefix = book.dir().uri + for (let i = 0; i < aliases.length; i++) { + if (aliases[i].uri.slice(0, prefix.length) === prefix) { + return aliases[i] + } + } + throw new Error('No local URI for ' + person) +} + +// Check every group is in the list and add it if not. +export async function checkDataModel (book, detailsSectionContent) { + // await kb.fetcher.load(groups) // asssume loaded already + const groups = await loadAllGroups(book) + + if (groups && groups.length > 0) { + + const { del, ins } = await getDataModelIssues(groups) + + if (del.length) { + UI.widgets.deleteButtonWithCheck( + dom, + detailsSectionContent, // where it appends it to + 'contact', + async function () { + await kb.updater.updateMany(del, ins) + debug.log('Deleted ' + del.length + ' bad statements from groups') + }) + } + } +} + +// Prepare book data once so askName forms load instantly +export async function ensureBookLoaded () { + const ourBook = findBookFromGroups(book) + try { + await kb.fetcher.load(ourBook) + } catch (err) { + throw new Error('Book won\'t load:' + ourBook) + } + const nameEmailIndex = kb.any(ourBook, ns.vcard('nameEmailIndex')) + if (!nameEmailIndex) throw new Error('No nameEmailIndex') + await kb.fetcher.load(nameEmailIndex) +} diff --git a/src/autocompleteBar.ts b/src/autocompleteBar.ts deleted file mode 100644 index dba7738..0000000 --- a/src/autocompleteBar.ts +++ /dev/null @@ -1,91 +0,0 @@ -// The Control with decorations - -import { NamedNode } from 'rdflib' -import { store } from 'solid-logic' -import { ns, widgets, icons } from 'solid-ui' -import { renderAutoComplete } from './autocompletePicker' // dbpediaParameters -import { wikidataParameters } from './publicData' - -const WEBID_NOUN = 'Solid ID' - -const kb = store - -// const AUTOCOMPLETE_THRESHOLD = 4 // don't check until this many characters typed -// const AUTOCOMPLETE_ROWS = 12 // 20? - -const GREEN_PLUS = icons.iconBase + 'noun_34653_green.svg' -const SEARCH_ICON = icons.iconBase + 'noun_Search_875351.svg' - -export async function renderAutocompleteControl (dom:HTMLDocument, - person:NamedNode, options, addOneIdAndRefresh): Promise { - async function autoCompleteDone (object, _name) { - const webid = object.uri - removeDecorated() - return addOneIdAndRefresh(person, webid) - } - - async function greenButtonHandler (_event) { - const webid = await widgets.askName(dom, store, creationArea, ns.vcard('url'), null, WEBID_NOUN) - if (!webid) { - return // cancelled by user - } - return addOneIdAndRefresh(person, webid) - } - function removeDecorated () { - if (decoratedAutocomplete) { - creationArea.removeChild(decoratedAutocomplete) - decoratedAutocomplete = null - } - } - async function searchButtonHandler (_event) { - if (decoratedAutocomplete) { - creationArea.removeChild(decoratedAutocomplete) - decoratedAutocomplete = null - } else { - decoratedAutocomplete = dom.createElement('div') - decoratedAutocomplete.setAttribute('style', 'display: flex; flex-flow: wrap;') - decoratedAutocomplete.appendChild(await renderAutoComplete(dom, acOptions, autoCompleteDone)) - decoratedAutocomplete.appendChild(acceptButton) - decoratedAutocomplete.appendChild(cancelButton) - creationArea.appendChild(decoratedAutocomplete) - } - } - - async function droppedURIHandler (uris) { - for (const webid of uris) { // normally one but can be more than one - await addOneIdAndRefresh(person, webid) - } - } - - const queryParams = options.queryParameters || wikidataParameters - const acceptButton = widgets.continueButton(dom) - const cancelButton = widgets.cancelButton(dom, removeDecorated) - const klass = options.class - const acOptions = { - queryParams, - class: klass, - acceptButton, - cancelButton - } - - var decoratedAutocomplete: HTMLDivElement | null = null - // const { dom } = dataBrowserContext - options = options || {} - options.editable = kb.updater.editable(person.doc().uri, kb) - - const creationArea = dom.createElement('div') - creationArea.setAttribute('style', 'display: flex; flex-flow: wrap;') - - if (options.editable) { - // creationArea.appendChild(await renderAutoComplete(dom, options, autoCompleteDone)) wait for searchButton - creationArea.style.width = '100%' - const plus = creationArea.appendChild(widgets.button(dom, GREEN_PLUS, options.idNoun, greenButtonHandler)) - widgets.makeDropTarget(plus, droppedURIHandler, null) - if (options.dbLookup) { - creationArea.appendChild(widgets.button(dom, SEARCH_ICON, options.idNoun, searchButtonHandler)) - } - } - return creationArea -} // renderAutocompleteControl - -// ends diff --git a/src/autocompleteField.ts b/src/autocompleteField.ts deleted file mode 100644 index 8ea182f..0000000 --- a/src/autocompleteField.ts +++ /dev/null @@ -1,177 +0,0 @@ -/* Form field for doing autocompleete - */ -import { BlankNode, NamedNode, st, Variable } from 'rdflib' -import { store } from 'solid-logic' -import { ns, style, widgets } from 'solid-ui' -import { renderAutoComplete } from './autocompletePicker' // dbpediaParameters - -// const AUTOCOMPLETE_THRESHOLD = 4 // don't check until this many characters typed -// const AUTOCOMPLETE_ROWS = 12 // 20? - -/** - * Render a autocomplete form field - * - * The same function is used for many similar one-value fields, with different - * regexps used to validate. - * - * @param dom The HTML Document object aka Document Object Model - * @param container If present, the created widget will be appended to this - * @param already A hash table of (form, subject) kept to prevent recursive forms looping - * @param subject The thing about which the form displays/edits data - * @param form The form or field to be rendered - * @param doc The web document in which the data is - * @param callbackFunction Called when data is changed? - * - * @returns The HTML widget created - */ - -export function autocompleteField ( // @@ are they allowed too be async?? - dom: HTMLDocument, - container: HTMLElement | undefined, - already, - subject: NamedNode | BlankNode | Variable, - form: NamedNode, - doc: NamedNode | undefined, - callbackFunction: (ok: boolean, errorMessage: string) => void -): HTMLElement { - async function addOneIdAndRefresh (result, _name) { - const ds = kb.statementsMatching(subject, property as any) // remove any multiple values - - let is = ds.map(statement => st(statement.subject, statement.predicate, result, statement.why)) // can include >1 doc - if (is.length === 0) { - // or none - is = [st(subject, property as any, result, doc)] - } - try { - await kb.updater.updateMany(ds, is) - } catch (err) { - callbackFunction(false, err) - box.appendChild(widgets.errorMessageBlock(dom, 'Autocomplete form data write error:' + err)) - return - } - callbackFunction(true, '') - } - - const kb = store - const formDoc = form.doc ? form.doc() : null // @@ if blank no way to know - - const box = dom.createElement('tr') - if (container) container.appendChild(box) - const lhs = dom.createElement('td') - lhs.setAttribute('class', 'formFieldName') - lhs.setAttribute('style', ' vertical-align: middle;') - box.appendChild(lhs) - const rhs = dom.createElement('td') - rhs.setAttribute('class', 'formFieldValue') - box.appendChild(rhs) - - const property = kb.any(form, ns.ui('property')) - if (!property) { - box.appendChild( - dom.createTextNode('Error: No property given for autocomplete field: ' + form) - ) - return box - } - const searchClass = kb.any(form, ns.ui('searchClass')) - if (!searchClass) { - box.appendChild( - dom.createTextNode('Error: No searchClass given for autocomplete field: ' + form) - ) - return box - } - const endPoint = kb.any(form, ns.ui('endPoint')) - if (!endPoint) { - box.appendChild( - dom.createTextNode('Error: No SPARQL endPoint given for autocomplete field: ' + form) - ) - return box - } - const queryTemplate = kb.any(form, ns.ui('queryTemplate')) - if (!queryTemplate) { - box.appendChild( - dom.createTextNode('Error: No queryTemplate given for autocomplete field: ' + form) - ) - return box - } - // It can be cleaner to just remove empty fields if you can't edit them anyway - const suppressEmptyUneditable = kb.anyJS(form, ns.ui('suppressEmptyUneditable'), null, formDoc) - - lhs.appendChild(widgets.fieldLabel(dom, property as any, form)) - const uri = widgets.mostSpecificClassURI(form) - let params = widgets.fieldParams[uri] - if (params === undefined) params = {} // non-bottom field types can do this - // const theStyle = params.style || style.textInputStyle - const klass = kb.the(form, ns.ui('category'), null, formDoc) as NamedNode - /* - { label: string; - logo: string; - searchByNameQuery?: string; - searchByNameURI?: string; - insitituteDetailsQuery?: string; - endPoint?: string; - class: object - } -*/ - - const searchByNameQuery = kb.the(form, ns.ui('searchByNameQuery'), null, formDoc) - - if (!klass) { - box.appendChild( - dom.createTextNode('Error: No class given for autocomplete field: ' + form) - ) - return box - } - - const queryParams = { label: 'from form', logo: '', class: klass, endPoint: endPoint.value, searchByNameQuery: searchByNameQuery?.value } - - const options = { // cancelButton?: HTMLElement, - // acceptButton?: HTMLElement, - class: klass, - queryParams - } - - // const acWiget = rhs.appendChild(await renderAutoComplete(dom, options, addOneIdAndRefresh)) - - // @@ set existing value is any - renderAutoComplete(dom, options, addOneIdAndRefresh).then(acWiget => rhs.appendChild(acWiget)) - - const field = dom.createElement('input') - ;(field as any).style = style.textInputStyle // Do we have to override length etc? - rhs.appendChild(field) - field.setAttribute('type', params.type ? params.type : 'text') - - const size = kb.any(form, ns.ui('size')) // Form has precedence - field.setAttribute( - 'size', - size ? '' + size : params.size ? '' + params.size : '20' - ) - const maxLength = kb.any(form, ns.ui('maxLength')) - field.setAttribute('maxLength', maxLength ? '' + maxLength : '4096') - - doc = doc || widgets.fieldStore(subject, property as any, doc) - - let obj = kb.any(subject, property as any, undefined, doc) - if (!obj) { - obj = kb.any(form, ns.ui('default')) - } - if (obj) { - /* istanbul ignore next */ - field.value = obj.value || obj.value || '' - } - field.setAttribute('style', style) - if (!kb.updater) { - throw new Error('kb has no updater') - } - if (!kb.updater.editable((doc as NamedNode).uri)) { - field.readOnly = true // was: disabled. readOnly is better - ;(field as any).style = style.textInputStyleUneditable - // backgroundColor = textInputBackgroundColorUneditable - if (suppressEmptyUneditable && field.value === '') { - box.style.display = 'none' // clutter - } - return box - } - return box -} - -// ends diff --git a/src/autocompletePicker.ts b/src/autocompletePicker.ts deleted file mode 100644 index 9b8f951..0000000 --- a/src/autocompletePicker.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* Create and edit data using public data -** -** organization conveys many distinct types of thing. -** -*/ -import { NamedNode } from 'rdflib' -import { store } from 'solid-logic' -import { style, widgets } from 'solid-ui' -import { - AUTOCOMPLETE_LIMIT, filterByLanguage, getPreferredLanguages, QueryParameters, queryPublicDataByName -} from './publicData' - -const AUTOCOMPLETE_THRESHOLD = 4 // don't check until this many characters typed -const AUTOCOMPLETE_ROWS = 20 // 20? -const AUTOCOMPLETE_ROWS_STRETCH = 40 -const AUTOCOMPLETE_DEBOUNCE_MS = 300 - -// const autocompleteRowStyle = 'border: 0.2em solid straw;' // @@ white - -/* -Autocomplete happens in four phases: - 1. The search string is too small to bother - 2. The search string is big enough, and we have not loaded the array - 3. The search string is big enough, and we have loaded array up to the limit - Display them and wait for more user input - 4. The search string is big enough, and we have loaded array NOT to the limit - but including all matches. No more fetches. - If user gets more precise, wait for them to select one - or reduce to a single - 5. Optionally waiting for accept button to be pressed -*/ - -type AutocompleteOptions = { - cancelButton?: HTMLElement, - acceptButton?: HTMLElement, - class: NamedNode, - queryParams: QueryParameters -} - -interface Callback1 { - (subject: NamedNode, name: string): void; -} - -// The core of the autocomplete UI -export async function renderAutoComplete (dom: HTMLDocument, options:AutocompleteOptions, // subject:NamedNode, predicate:NamedNode, - callback: Callback1) { - function complain (message) { - const errorRow = table.appendChild(dom.createElement('tr')) - console.log(message) - errorRow.appendChild(widgets.errorMessageBlock(dom, message, 'pink')) - style.setStyle(errorRow, 'autocompleteRowStyle') - errorRow.style.padding = '1em' - } - /* - function remove (ele?: HTMLElement) { - if (ele && ele.parentNode) { - ele.parentNode.removeChild(ele) - } - } - */ - function finish (object, name) { - console.log('Auto complete: finish! ' + object) - // remove(options.cancelButton) - // remove(options.acceptButton) - // remove(div) - callback(object, name) - } - async function gotIt (object:NamedNode, name:string) { - if (options.acceptButton) { - (options.acceptButton as any).disabled = false - searchInput.value = name // complete it - foundName = name - foundObject = object - console.log('Auto complete: name: ' + name) - console.log('Auto complete: waiting for accept ' + object) - return - } - finish(object, name) - } - - async function acceptButtonHandler (_event) { - if (searchInput.value === foundName) { // still - finish(foundObject, foundName) - } else { - (options.acceptButton as any).disabled = true - } - } - - /* - async function cancelButtonHandler (_event) { - console.log('Auto complete: Canceled by user! ') - div.innerHTML = '' // Clear out the table - } - */ - - function nameMatch (filter:string, candidate: string):boolean { - const parts = filter.split(' ') // Each name part must be somewhere - for (let j = 0; j < parts.length; j++) { - const word = parts[j] - if (candidate.toLowerCase().indexOf(word) < 0) return false - } - return true - } - - /* - function cancelText (_event) { - searchInput.value = '' - if (options.acceptButton) { - (options.acceptButton as any).disabled = true // start again - } - _candidatesLoaded = false - } - */ - - function thinOut (filter) { - let hits = 0 - let pick: string | null = null; let pickedName = '' - for (let j = table.children.length - 1; j > 0; j--) { // backwards as we are removing rows - const row = table.children[j] - if (nameMatch(filter, row.textContent)) { - hits += 1 - pick = row.getAttribute('subject') - pickedName = row.textContent - ;(row as any).style.display = '' - ;(row as any).style.color = 'blue' // @@ chose color - } else { - ;(row as any).style.display = 'none' - } - } - if (hits === 1 && pick) { // Maybe require green confirmation button be clicked? - console.log(` auto complete elimination: "${filter}" -> "${pickedName}"`) - gotIt(store.sym(pick), pickedName) // uri, name - } - } - - function clearList () { - while (table.children.length > 1 && table.lastChild) { - table.removeChild(table.lastChild) - } - } - - async function inputEventHHandler (_event) { - if (runningTimeout) { - clearTimeout(runningTimeout) - } - setTimeout(refreshList, AUTOCOMPLETE_DEBOUNCE_MS) - } - - async function refreshList () { - if (inputEventHandlerLock) { - console.log(`Ignoring "${searchInput.value}" because of lock `) - return - } - inputEventHandlerLock = true - const languagePrefs = await getPreferredLanguages() - const filter = searchInput.value.trim().toLowerCase() - if (filter.length < AUTOCOMPLETE_THRESHOLD) { // too small - clearList() - candidatesLoaded = false - numberOfRows = AUTOCOMPLETE_ROWS - } else { - if (allDisplayed && lastFilter && filter.startsWith(lastFilter)) { - thinOut(filter) // reversible? - inputEventHandlerLock = false - return - } - let bindings - try { - bindings = await queryPublicDataByName(filter, OrgClass, options.queryParams) - // bindings = await queryDbpedia(sparql) - } catch (err) { - complain('Error querying db of organizations: ' + err) - inputEventHandlerLock = false - return - } - candidatesLoaded = true - const loadedEnough = bindings.length < AUTOCOMPLETE_LIMIT - if (loadedEnough) { - lastFilter = filter - } else { - lastFilter = null - } - clearList() - const slimmed = filterByLanguage(bindings, languagePrefs) - if (loadedEnough && slimmed.length <= AUTOCOMPLETE_ROWS_STRETCH) { - numberOfRows = slimmed.length // stretch if it means we get all items - } - allDisplayed = loadedEnough && slimmed.length <= numberOfRows - console.log(` Filter:"${filter}" bindings: ${bindings.length}, slimmed to ${slimmed.length}; rows: ${numberOfRows}, Enough? ${loadedEnough}, All displayed? ${allDisplayed}`) - slimmed.slice(0, numberOfRows).forEach(binding => { - const row = table.appendChild(dom.createElement('tr')) - style.setStyle(row, 'autocompleteRowStyle') - const uri = binding.subject.value - const name = binding.name?.value - row.setAttribute('style', 'padding: 0.3em;') - row.setAttribute('subject', uri) - row.style.color = allDisplayed ? '#080' : '#000' // green means 'you should find it here' - row.textContent = name || '' - row.addEventListener('click', async _event => { - console.log(' click row textContent: ' + row.textContent) - console.log(' click name: ' + name) - gotIt(store.sym(uri), name || '') - }) - }) - } - inputEventHandlerLock = false - } // refreshList - - /* sparqlForSearch -* -* name -- e.g., "mass" -* theType -- e.g., -*/ - /* - function sparqlForSearch (name:string, theType:NamedNode):string { - const clean = name.replace(/\W/g, '') // Remove non alphanum so as to protect regexp - const sparql = `select distinct ?subject, ?name where { - ?subject a <${theType.uri}>; rdfs:label ?name - FILTER regex(?name, "${clean}", "i") - } LIMIT ${AUTOCOMPLETE_LIMIT}` - return sparql - } -*/ - const OrgClass = options.class // kb.sym('http://umbel.org/umbel/rc/EducationalOrganization') // @@@ other - if (options.acceptButton) { - options.acceptButton.addEventListener('click', acceptButtonHandler, false) - } - if (options.cancelButton) { - // options.cancelButton.addEventListener('click', cancelButtonHandler, false) - } - - // @ts-ignore - let candidatesLoaded = false - const runningTimeout = null - let inputEventHandlerLock = false - let allDisplayed = false - var lastFilter: string | null = null - var numberOfRows = AUTOCOMPLETE_ROWS - const div = dom.createElement('div') - var foundName: string | null = null // once found accepted string must match this - var foundObject: NamedNode | null = null - var table = div.appendChild(dom.createElement('table')) - table.setAttribute('style', 'max-width: 30em; margin: 0.5em;') - const head = table.appendChild(dom.createElement('tr')) - style.setStyle(head, 'autocompleteRowStyle') - const cell = head.appendChild(dom.createElement('td')) - const searchInput = cell.appendChild(dom.createElement('input')) - searchInput.setAttribute('type', 'text') - const searchInputStyle = style.searchInputStyle || - 'border: 0.1em solid #444; border-radius: 0.5em; width: 100%; font-size: 100%; padding: 0.1em 0.6em' // @ - searchInput.setAttribute('style', searchInputStyle) - searchInput.addEventListener('keyup', function (event) { - if (event.keyCode === 13) { - acceptButtonHandler(event) - } - }, false) - - searchInput.addEventListener('input', inputEventHHandler) - return div -} // renderAutoComplete22q1 diff --git a/src/card.ai b/src/card.ai deleted file mode 100644 index 7c6fc22..0000000 --- a/src/card.ai +++ /dev/null @@ -1,980 +0,0 @@ -%PDF-1.5 % -1 0 obj <>/OCGs[5 0 R 23 0 R 40 0 R 57 0 R 74 0 R 91 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream - - - - - application/pdf - - - Web - - - - - Adobe Illustrator CS5 - 2015-03-18T15:23:14-04:00 - 2015-08-10T17:45:42-04:00 - 2015-08-10T17:45:42-04:00 - - - - 256 - 160 - JPEG - /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAoAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A8qYq7FXYq7FXYq7FURFq F/CgjhuZY4x0RHZRv7A4qv8A0tqv/LZP/wAjH/rirv0tqv8Ay2T/APIx/wCuKu/S2q/8tk//ACMf +uKu/S2q/wDLZP8A8jH/AK4q79Lar/y2T/8AIx/64q79Lar/AMtk/wDyMf8Arirv0tqv/LZP/wAj H/rirv0tqv8Ay2T/APIx/wCuKu/S2q/8tk//ACMf+uKu/S2q/wDLZP8A8jH/AK4q79Lar/y2T/8A Ix/64q79Lar/AMtk/wDyMf8AriqvD5l8xwJ6cOq3kSdeKXEqip9g2Kr/APFnmn/q833/AEkzf81Y q7/Fnmn/AKvN9/0kzf8ANWKu/wAWeaf+rzff9JM3/NWKu/xZ5p/6vN9/0kzf81Yq7/Fnmn/q833/ AEkzf81Yq/QP8ibi4ufyh8rT3ErzTyWSmSWRizseTbljUnFWeYq7FXYq7FXYq7FX5V4q/RfyH5D8 jT+RvLs8/l3TJZpdMs3llezt2Zma3QszMUqST1OKp7/yrzyB/wBSzpX/AEg23/NGKu/5V55A/wCp Z0r/AKQbb/mjFXf8q88gf9SzpX/SDbf80Yq7/lXnkD/qWdK/6Qbb/mjFUPL+Vn5YzSGSXyhokkjf ad9OtGJoKbkx4qt/5VP+Vn/Um6H/ANw2z/6p4q7/AJVP+Vn/AFJuh/8AcNs/+qeKu/5VP+Vn/Um6 H/3DbP8A6p4q7/lU/wCVn/Um6H/3DbP/AKp4q7/lU/5Wf9Sbof8A3DbP/qnirv8AlU/5Wf8AUm6H /wBw2z/6p4q7/lU/5Wf9Sbof/cNs/wDqnirv+VT/AJWf9Sbof/cNs/8Aqnirv+VT/lZ/1Juh/wDc Ns/+qeKu/wCVT/lZ/wBSbof/AHDbP/qnirv+VT/lZ/1Juh/9w2z/AOqeKu/5VP8AlZ/1Juh/9w2z /wCqeKoeb8mfyllcu3k/SAT2SzhQbf5KqBiqz/lSn5R/9ShpX/SLF/TFXf8AKlPyj/6lDSv+kWL+ mKu/5Up+Uf8A1KGlf9IsX9MVd/ypT8o/+pQ0r/pFi/pir8+/PFrbWnnXzBa2saw21vqV5FBCgoqI k7qqqB0AAoMVffX5A/8AkmvKf/MCv/EmxVn+KuxV2KuxV2KuxV+VeKv00/Lz/lAPLP8A2yrH/qGT FWQYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX5l/mF/yn3mX/tq33/U S+KvvX8gf/JNeU/+YFf+JNirP8VdirsVdirsVdir8q8Vfpp+Xn/KAeWf+2VY/wDUMmKsgxV2KuxV Surq1tIHuLqZLeCMVeWVgiKPEsxAGKsP1H86/wApNPZkufNumc1NGWG4ScgjqCIS/hiqR3H/ADk5 +RsH2vM6OSKgR2t7J+KwkD6cVULL/nKb8kbq4WH9PNAW2V5rS6RK1pQt6ZA+Z2xV6bpWr6Vq9hFq GlXkN/YzisN1bSLLGw9mQkYqi8VdirsVdirsVdirsVdirsVdirsVdirsVdir8y/zC/5T7zL/ANtW +/6iXxV96/kD/wCSa8p/8wK/8SbFWf4q7FXYq7FXYq7FX5aakiR6jdIgCok0iqo6ABiAMVfpF+VL u/5XeT3diztommlmJqSTaR1JOKspxV2Kvjf8+P8AnJTzv/jbUPL/AJS1FtJ0jR5ntJJoFT1rieE8 ZWaRgxCK4KqFpUbmvZV4f5o88eb/ADVOk/mLV7rU3jAEQuJCyJQU+BPsL70G+KpVBZXtx/vPbyTf 8Y0Zv1DFUanlfzG+66Zc/TE6/rAxVZfeXdcsYTPd2UsMI2MhX4RXYVI6Yq9F/wCcevzdvPIHnK3h u7lh5X1WRYdWgY/u4y3wpdAfstGaciOqVHhRV9+o6OiujBkYAqwNQQdwQRireKuxV8eeev8AnLD8 0ND87+YdFsotMNnpep3llbGS3kZ/St7h4k5ESip4qK7Yqkf/AEOT+bn++dK/6Rpf+q2Ku/6HJ/Nz /fOlf9I0v/VbFXv3/ONv5seaPzG0TWb3zAlsk1hcxwwfVY2jXi8fI8gzvU1xVA/85MfnJ5u/Lb/D n+HktH/Sv13619biaWn1b6vw4cXjp/fNXFXh/wD0OT+bn++dK/6Rpf8Aqtirv+hyfzc/3zpX/SNL /wBVsVd/0OT+bn++dK/6Rpf+q2Kpr5U/5y2/NPVfNOjaXdRaYLa/vra2nKW8gbhNMqNxJlNDRtsV fZWKuxV+Zf5hf8p95l/7at9/1Evir71/IH/yTXlP/mBX/iTYqz/FXYq7FXYq7FXYq/LbVv8Ajq3n /GeT/iZxV+j/AOU//krPJv8A2w9N/wCoOPFWVYq7FX5baretf6peXzElrueSdi2xJkctvT54q9S8 s+S9AgsbK+e2E13JDHI7SkuodkBNFPw9em2KspAAAAFANgBireKqN3awXdrLazrzhmUpIviGFMVe G67pUmk6tc6e7BzAwCuO6sAyn58SMVfb3/OJPmTVNb/KOKPUHaQ6PezadayvUs1vHHFLGKnsnrFB 7KBir2fFXYq+ffM//OHnlvX/ADJq2uzeYbyGXVry4vpIUiiKo1zK0pVSd6AvTFUt/wChIfK3/Uy3 3/ImHFXf9CQ+Vv8AqZb7/kTDir1P8nvye038sdN1CxsdQm1BNQmSd3nREKlE40HHFUN+cv5IaV+a P6H+v6nPp36H+s+n6CI/P6z6VeXPpx9AU+eKvNf+hIfK3/Uy33/ImHFXf9CQ+Vv+plvv+RMOKu/6 Eh8rf9TLff8AImHFUboX/OG3lnSNb0/Vo/MV7LJp9zDdJE0MQVmgkEgUkdjxxV9D4q7FX5l/mF/y n3mX/tq33/US+KvvX8gf/JNeU/8AmBX/AIk2Ks/xV2KuxV2KuxV2Kvy21b/jq3n/ABnk/wCJnFX6 P/lP/wCSs8m/9sPTf+oOPFWVYq7FX5da/pFxo2uajpFypS40+5mtZVbqGhcofD+XFXsPlK+S98ua fMvURLE4/wAqL4D+K4qm+KuxVxIAqdgOpxV4n5zuba58zX01tKJoWZeMi7gkRqDQ+xFMVfZH/OHb E/lAQYwgXU7oBgKcxxjPI+PXj9GKvccVdirsVdirsVdirsVdirsVdirsVdirsVfmX+YX/KfeZf8A tq33/US+KvvX8gf/ACTXlP8A5gV/4k2Ks/xV2KuxV2KuxV2Kvy21b/jq3n/GeT/iZxV+j/5T/wDk rPJv/bD03/qDjxVlWKuxV8O/85a+QL/QvzHm8xpCTo/mIJLHOo+BLqNAk0TEdGbh6gr15HwOKvNP J3nSbQ3NtcKZtOkbkyj7cbHqyfxGKvWbG+tb61jurSQSwSiqOP8APYjwxVXxV475o85atqdzcW6T GHTg7IkCfDyUGlXPU18OmKq/5d/lj5u8/wCsjTfL9oZFQg3d9JVbe3Q/tSvT7lFWPYYq/QP8uPIu neRvJuneWbCQzR2SN6tywCtLNIxeWQgVpVmNBXYUHbFWS4q7FXiHmH/nLv8ALbQdf1PQ7zTdZku9 Ku57K4eGC1MbSW8jROULXKsVLLtUD5Yql/8A0Or+Vn/Vq1z/AKR7P/srxV3/AEOr+Vn/AFatc/6R 7P8A7K8Vd/0Or+Vn/Vq1z/pHs/8AsrxV3/Q6v5Wf9WrXP+kez/7K8Vd/0Or+Vn/Vq1z/AKR7P/sr xVkn5e/85NeQ/Pfmm28taRYapBfXSSvHJdxW6RAQxmRqmO4lbou3w4q9bxV2KuxV2KvzL/ML/lPv Mv8A21b7/qJfFX3r+QP/AJJryn/zAr/xJsVZ/irsVdirsVdirsVfltq3/HVvP+M8n/Ezir9H/wAp /wDyVnk3/th6b/1Bx4qyrFXYqlXmfyvoPmjRLnRNdtEvdNulpLC9RuN1ZWFGVlO4ZTUYq+Gfz2/I XU/y0vYry2nbUPLN9IUtLxlpJFJuwgnp8PLiPhYbNQ7DpirFvy58xPY6mNNmb/RL1uKA9Em/ZI/1 vsn6MVer4q+eWYsxYmpJqTir9J/yp0jSdL/Lny5DplpDaQy6daTyrAioHllgRnkbiPidyaljucVZ ZirsVdir4p/MD/nGX84NY8+eZNXsNKhksdR1S9u7SQ3dupaKe4eSMlWcEVVhscVSH/oU/wDO3/qz wf8ASZbf9VMVd/0Kf+dv/Vng/wCky2/6qYq7/oU/87f+rPB/0mW3/VTFXf8AQp/52/8AVng/6TLb /qpirv8AoU/87f8Aqzwf9Jlt/wBVMVei/kD+QP5n+T/zP07Xte06K30y3iuUllS5glIMsDovwo7N 9psVfV+KuxV2KuxV+Zf5hf8AKfeZf+2rff8AUS+KvvX8gf8AyTXlP/mBX/iTYqz/ABV2KuxV2Kux V2Kvy21b/jq3n/GeT/iZxV+j/wCU/wD5Kzyb/wBsPTf+oOPFWVYq7FXYqxb80PKll5r8ga5od2oY XNrI0DmnwTxL6kMg/wBWRQcVfmtHI8ciyIeLoQysOoINQcVfQFrMZraKYihkRXI8OQrirwrW7J7H V7y0YU9GV1H+rWqn6VocVfeH/OMnne18z/lRpVuJF+v6Ci6XeQg7qsA427U60aHjv4gjtir1fFXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq/Mv8AML/lPvMv/bVvv+ol8Vfev5A/+Sa8p/8AMCv/ABJs VZ/irsVdirsVdirsVfltq3/HVvP+M8n/ABM4q/R/8p//ACVnk3/th6b/ANQceKsqxV2KuxViX5te ZU8tflr5j1otwe2sZVt26fv5h6MH/JWRcVfm5bwyTzxwRiskrKiD3Y0GKvoCGJYoUiX7MahV+Sim KvPvzQ0DePWoF8Irun3I/wDxr92Kpn/zjd+Zh8j/AJiWy3c3p6FrRWy1TkaInI/uJzXp6Ujbn+Ut ir7+xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KvzL/ML/lPvMv8A21b7/qJfFX3r+QP/AJJryn/z Ar/xJsVZ/irsVdirsVdirsVfltq3/HVvP+M8n/Ezir9H/wAp/wDyVnk3/th6b/1Bx4qyrFXYq7FX zX/zlr+aflGfyXJ5N0zVIr3Wp7uFr23tj6ixRQkuRLIvwK3ML8Na+2Kvlfyg1uvmbTTOKp66gU/n O0f/AA9MVe34qoX9lBfWU1nOOUM6FHHsR1HuO2KvC9X0y40vUZ7G4H7yFqBuzL1Vh7Eb4q+1f+cX fzlj83+WU8s6vPXzLokQRWdqtdWaUVJRXcvHskn0N+0aKvc8VdirsVdirsVdirsVdirsVdirsVdi rsVfmX+YX/KfeZf+2rff9RL4q+9fyB/8k15T/wCYFf8AiTYqz/FXYq7FXYq7FXYq/LbVv+Oref8A GeT/AImcVfo/+U//AJKzyb/2w9N/6g48VZVirzv87Pzh038svLcV9JB9d1a/ZotKsSeKuyAF5JG6 iOMMK03JIHeoVfGHnb8+fzS84+rFqmtywWEta6dY/wCi2/E/sMI6PIu/+7GbFWEafpmoajOILKB5 5T1CDYe7HoB7nFXo/lT8u1064i1DUpRLdRHlFAn2EbsSx+0R/nXFWbYq7FWI/mH5dhv9LfUUIS7s ULcv54huVPy6jFWC+QdT1DTPOuh3un3ElrdR30ASaJirAPIFYVHZlJBHcbYq/TXFXYq/Nb82P/Jp +cv+25qX/UZJirFMVdirsVdirsVdir0D8gf/ACcnlP8A5jl/4i2Kv0UxV2KuxV+Zf5hf8p95l/7a t9/1Evir71/IH/yTXlP/AJgV/wCJNirP8VdirsVdirsVdir8wPNYC+aNYAFAL65AA6Aes2Kv0L/J Qk/lH5Qr/wBWq1/5NDFWaYq+Uf8AnOS2kFx5OutzGyX8R2NAym3br/lcvwxV4F+Xuj6ZqurTQ38X rLFCZY0LFRUMq78SK/axV6xa2dpaQiG1hSCIdEjUKPwxVWxV2KuxVKvNX/KN6n/zDyf8ROKvIvKf /KU6N/zHW3/J5cVfp/irsVSO48i+SLm4lubny9pk9xO7STTSWdu7u7nkzMzISWJNSTiqn/yrzyB/ 1LOlf9INt/zRirv+VeeQP+pZ0r/pBtv+aMVd/wAq88gf9SzpX/SDbf8ANGKu/wCVeeQP+pZ0r/pB tv8AmjFXf8q88gf9SzpX/SDbf80Yq7/lXnkD/qWdK/6Qbb/mjFVaz8k+TLK5ju7PQNOtrqE8op4b SCORG8VZUBB+WKp1irsVdir8y/zC/wCU+8y/9tW+/wCol8Vfev5A/wDkmvKf/MCv/EmxVn+KuxV2 KuxV2KuxV+YHmz/lKdZ/5jrn/k82Kv0K/JT/AMlH5Q/7ZVr/AMmhirNcVfKH/OceoK135Q05W+KO O+uJF7UkMCIf+SbYq+YbS+vbORpLSeS3kZeDPExRitQaVUg9RiqrJrGryf3l9cPvX4pXO/0nFVWy 8w63ZIyWt7LErnk4DVqfHeuKon/GXmj/AKuM33j+mKu/xl5o/wCrjN94/piqnceavMVxA8E9/LJD KpWRCRQg9QdsVa8p/wDKU6N/zHW3/J5cVfp/irsVfB35kfnd+bGnfmJ5p0+x8z3lvZWer39vawIy 8Y4ormRERfh6KoAxVjn/ACv785P+psvv+CX/AJpxV3/K/vzk/wCpsvv+CX/mnFX03/ziV5382ebP LuvXHmPU5tTntruKOCScglEaKpAoB3xV7zirsVdirsVdirsVdir8y/zC/wCU+8y/9tW+/wCol8Vf ev5A/wDkmvKf/MCv/EmxVn+KuxV2KuxV2KuxV+YHmz/lKdZ/5jrn/k82Kv0K/JT/AMlH5Q/7ZVr/ AMmhirNcVYB+YH5HeQvP2tW2r+ZIbie5tIFtYkineKP01dpN1XuTIamuKpRa/wDOLn5HW/Eny6Zn Ukhpby9br4qJgp+7FV+of84xfkperEp8vi3EIYD6vcXEZbka/GRJVvauKoP/AKFP/JL/AKtE/wD0 mXP/ADXirv8AoU/8kv8Aq0T/APSZc/8ANeKu/wChT/yS/wCrRP8A9Jlz/wA14q7/AKFP/JL/AKtE /wD0mXP/ADXiqtZ/84tfkzZ3kF3b6TOs9tIksTG8uSA6MGU0L+IxV6zirsVeWaz/AM4y/lBrGr32 r3+lTSX2o3Et3dyC7uFDSzuZJCFVwBVmOwxVB/8AQp/5Jf8AVon/AOky5/5rxV3/AEKf+SX/AFaJ /wDpMuf+a8VZr5A/LLyf5Cs7u08s2r2sF7IstwryyTEuq8QQZGam2KsqxV2KuxV2KuxV2KuxV+Zf 5hf8p95l/wC2rff9RL4q+9fyB/8AJNeU/wDmBX/iTYqz/FXYq7FXYq7FXYq/MDzZ/wApTrP/ADHX P/J5sVfoV+Sn/ko/KH/bKtf+TQxVmuKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV+Zf5hf8AKfeZf+2rff8AUS+KvvX8gf8AyTXlP/mBX/iTYqz/ABV2KuxV2KuxV2KvzA82 f8pTrP8AzHXP/J5sVfoV+Sn/AJKPyh/2yrX/AJNDFWa4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FX5l/mF/yn3mX/tq33/US+KvvX8gf/JNeU/+YFf+JNirP8VdirsVdirs Vdir8t9Zvkv9Xvr5FKJd3Es6odyBI5YA/KuKvpbyH/zl55c8teS9F8vz+X7y4m0qzhtZJ0liCu0S BSwB3oaYqn3/AEO95W/6lq+/5HQ4q7/od7yt/wBS1ff8jocVd/0O95W/6lq+/wCR0OKu/wCh3vK3 /UtX3/I6HFXf9DveVv8AqWr7/kdDirv+h3vK3/UtX3/I6HFXf9DveVv+pavv+R0OKu/6He8rf9S1 ff8AI6HFXf8AQ73lb/qWr7/kdDirv+h3vK3/AFLV9/yOhxV3/Q73lb/qWr7/AJHQ4q7/AKHe8rf9 S1ff8jocVd/0O95W/wCpavv+R0OKu/6He8rf9S1ff8jocVd/0O95W/6lq+/5HQ4q7/od7yt/1LV9 /wAjocVd/wBDveVv+pavv+R0OKu/6He8rf8AUtX3/I6HFXf9DveVv+pavv8AkdDirv8Aod7yt/1L V9/yOhxV3/Q73lb/AKlq+/5HQ4q7/od7yt/1LV9/yOhxV8o+ZdVj1fzHqurRxmKPULy4ukiYgsqz ytIFJHccsVfoH+QP/kmvKf8AzAr/AMSbFWf4q7FXYq7FXYq7FXxB/wBCbfm5/v7Sv+kmX/qjirv+ hNvzc/39pX/STL/1RxV3/Qm35uf7+0r/AKSZf+qOKu/6E2/Nz/f2lf8ASTL/ANUcVd/0Jt+bn+/t K/6SZf8Aqjirv+hNvzc/39pX/STL/wBUcVd/0Jt+bn+/tK/6SZf+qOKu/wChNvzc/wB/aV/0ky/9 UcVd/wBCbfm5/v7Sv+kmX/qjirv+hNvzc/39pX/STL/1RxV3/Qm35uf7+0r/AKSZf+qOKu/6E2/N z/f2lf8ASTL/ANUcVd/0Jt+bn+/tK/6SZf8Aqjirv+hNvzc/39pX/STL/wBUcVd/0Jt+bn+/tK/6 SZf+qOKu/wChNvzc/wB/aV/0ky/9UcVd/wBCbfm5/v7Sv+kmX/qjirv+hNvzc/39pX/STL/1RxV3 /Qm35uf7+0r/AKSZf+qOKu/6E2/Nz/f2lf8ASTL/ANUcVd/0Jt+bn+/tK/6SZf8Aqjirv+hNvzc/ 39pX/STL/wBUcVd/0Jt+bn+/tK/6SZf+qOKu/wChNvzc/wB/aV/0ky/9UcVd/wBCbfm5/v7Sv+km X/qjir61/KzyzqPlf8vdC8v6kYzf6dbCG4MLFo+QYn4WIUkb+GKsqxV2KuxV2KuxV2Kv/9k= - - - - - - proof:pdf - uuid:65E6390686CF11DBA6E2D887CEACB407 - xmp.did:F77F117407206811822A938BEAB82E55 - uuid:537ad86f-15a7-4f47-8fc5-01a42f8bf30c - - uuid:52d3fbd8-d8d7-2543-8e0c-81d9ea137ecb - xmp.did:8CF5709C0E20681188C6A12CE4B46A4D - uuid:65E6390686CF11DBA6E2D887CEACB407 - proof:pdf - - - - - saved - xmp.iid:F77F117407206811822A938BEAB82E55 - 2015-03-18T15:23:14-04:00 - Adobe Illustrator CS5 - / - - - - - - Web - Document - - - 1 - False - False - - 24.000000 - 24.000000 - Pixels - - - - Cyan - Magenta - Yellow - Black - - - - - - Default Swatch Group - 0 - - - - White - RGB - PROCESS - 255 - 255 - 255 - - - Black - RGB - PROCESS - 0 - 0 - 0 - - - RGB Red - RGB - PROCESS - 255 - 0 - 0 - - - RGB Yellow - RGB - PROCESS - 255 - 255 - 0 - - - RGB Green - RGB - PROCESS - 0 - 255 - 0 - - - RGB Cyan - RGB - PROCESS - 0 - 255 - 255 - - - RGB Blue - RGB - PROCESS - 0 - 0 - 255 - - - RGB Magenta - RGB - PROCESS - 255 - 0 - 255 - - - R=193 G=39 B=45 - RGB - PROCESS - 193 - 39 - 45 - - - R=237 G=28 B=36 - RGB - PROCESS - 237 - 28 - 36 - - - R=241 G=90 B=36 - RGB - PROCESS - 241 - 90 - 36 - - - R=247 G=147 B=30 - RGB - PROCESS - 247 - 147 - 30 - - - R=251 G=176 B=59 - RGB - PROCESS - 251 - 176 - 59 - - - R=252 G=238 B=33 - RGB - PROCESS - 252 - 238 - 33 - - - R=217 G=224 B=33 - RGB - PROCESS - 217 - 224 - 33 - - - R=140 G=198 B=63 - RGB - PROCESS - 140 - 198 - 63 - - - R=57 G=181 B=74 - RGB - PROCESS - 57 - 181 - 74 - - - R=0 G=146 B=69 - RGB - PROCESS - 0 - 146 - 69 - - - R=0 G=104 B=55 - RGB - PROCESS - 0 - 104 - 55 - - - R=34 G=181 B=115 - RGB - PROCESS - 34 - 181 - 115 - - - R=0 G=169 B=157 - RGB - PROCESS - 0 - 169 - 157 - - - R=41 G=171 B=226 - RGB - PROCESS - 41 - 171 - 226 - - - R=0 G=113 B=188 - RGB - PROCESS - 0 - 113 - 188 - - - R=46 G=49 B=146 - RGB - PROCESS - 46 - 49 - 146 - - - R=27 G=20 B=100 - RGB - PROCESS - 27 - 20 - 100 - - - R=102 G=45 B=145 - RGB - PROCESS - 102 - 45 - 145 - - - R=147 G=39 B=143 - RGB - PROCESS - 147 - 39 - 143 - - - R=158 G=0 B=93 - RGB - PROCESS - 158 - 0 - 93 - - - R=212 G=20 B=90 - RGB - PROCESS - 212 - 20 - 90 - - - R=237 G=30 B=121 - RGB - PROCESS - 237 - 30 - 121 - - - R=199 G=178 B=153 - RGB - PROCESS - 199 - 178 - 153 - - - R=153 G=134 B=117 - RGB - PROCESS - 153 - 134 - 117 - - - R=115 G=99 B=87 - RGB - PROCESS - 115 - 99 - 87 - - - R=83 G=71 B=65 - RGB - PROCESS - 83 - 71 - 65 - - - R=198 G=156 B=109 - RGB - PROCESS - 198 - 156 - 109 - - - R=166 G=124 B=82 - RGB - PROCESS - 166 - 124 - 82 - - - R=140 G=98 B=57 - RGB - PROCESS - 140 - 98 - 57 - - - R=117 G=76 B=36 - RGB - PROCESS - 117 - 76 - 36 - - - R=96 G=56 B=19 - RGB - PROCESS - 96 - 56 - 19 - - - R=66 G=33 B=11 - RGB - PROCESS - 66 - 33 - 11 - - - - - - Grays - 1 - - - - R=0 G=0 B=0 - RGB - PROCESS - 0 - 0 - 0 - - - R=26 G=26 B=26 - RGB - PROCESS - 26 - 26 - 26 - - - R=51 G=51 B=51 - RGB - PROCESS - 51 - 51 - 51 - - - R=77 G=77 B=77 - RGB - PROCESS - 77 - 77 - 77 - - - R=102 G=102 B=102 - RGB - PROCESS - 102 - 102 - 102 - - - R=128 G=128 B=128 - RGB - PROCESS - 128 - 128 - 128 - - - R=153 G=153 B=153 - RGB - PROCESS - 153 - 153 - 153 - - - R=179 G=179 B=179 - RGB - PROCESS - 179 - 179 - 179 - - - R=204 G=204 B=204 - RGB - PROCESS - 204 - 204 - 204 - - - R=230 G=230 B=230 - RGB - PROCESS - 230 - 230 - 230 - - - R=242 G=242 B=242 - RGB - PROCESS - 242 - 242 - 242 - - - - - - Web Color Group - 1 - - - - R=63 G=169 B=245 - RGB - PROCESS - 63 - 169 - 245 - - - R=122 G=201 B=67 - RGB - PROCESS - 122 - 201 - 67 - - - R=255 G=147 B=30 - RGB - PROCESS - 255 - 147 - 30 - - - R=255 G=29 B=37 - RGB - PROCESS - 255 - 29 - 37 - - - R=255 G=123 B=172 - RGB - PROCESS - 255 - 123 - 172 - - - R=189 G=204 B=212 - RGB - PROCESS - 189 - 204 - 212 - - - - - - - - - Adobe PDF library 9.90 - - - - - - - - - - - - - - - - - - - - - - - - - endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 97 0 R/TrimBox[0.0 0.0 24.0 24.0]/Type/Page>> endobj 93 0 obj <>stream -HtTKn[1 e(j|I@޾C='y)\ǡBBi -OEK^(x;6Z1Spw-"K$qX_fcOH4;|/zY-)[]6zh CK9)>P:6imZ=g_A[0+1IwS8)ZIZ"ЋCycR!Wetj-jȠ|!,xU`sHs aDja`q5 uÍ+ljBNHg0v1C'DpLԳV]Y$G'UI !,3`ts*Ns.#.Ўp(ƎwTets#diu-%;ӎ!ɹ-s=+S(؊+lӅsX}Zu.VmHI/^Lj_AvW\ endstream endobj 97 0 obj <>stream -8;Ue`J-(6$!rr<-!!*~> endstream endobj 98 0 obj [/Indexed/DeviceRGB 255 99 0 R] endobj 99 0 obj <>stream -8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 91 0 obj <> endobj 100 0 obj [/View/Design] endobj 101 0 obj <>>> endobj 96 0 obj <> endobj 95 0 obj [/ICCBased 102 0 R] endobj 102 0 obj <>stream -HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  - 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 -V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= -x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- -ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 -N')].uJr - wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 -n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! -zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 94 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <>stream -%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.0 %%For: (Tim Berners-Lee) () %%Title: (card.ai) %%CreationDate: 2015/8/10 17:45 %%Canvassize: 16383 %%BoundingBox: -1 -19 30 -1 %%HiResBoundingBox: -0.001953 -19 29.2188 -1.04395 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 -24 24 0 %AI3_TemplateBox: 12.5 -12.5 12.5 -12.5 %AI3_TileBox: -276 -368 300 366 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -9 3 29.45 1318 756 18 0 0 403 387 0 0 0 1 0 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:-388 -312 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 105 0 obj <>stream -%%BoundingBox: -1 -19 30 -1 %%HiResBoundingBox: -0.001953 -19 29.2188 -1.04395 %AI7_Thumbnail: 128 80 8 %%BeginData: 6872 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD1AF827F8272727F8272727F8272727F8272727F8272727F82727 %27F8272727F8272727F8272727F8272727F8272727F8272727F827275227 %52275227522752275227522752275227522752277DFD1BFFFD64F827FD1B %FFFD64F852FD1BFFFD64F827FD1BFFFD04F8527DA87D7D7DA87D7D7DA87D %7D7DA87D7D7DA87D7D527D527D527D527D527D527D527D527D527D527D52 %7D527D527D527D527D527D527D527D527D527D527D527D527D527D527D52 %7DFD135227F8F8F852FD1BFFFD04F87DFD5BFF52F8F8F827FD1BFFFD04F8 %7DFD5BFF52F8F8F852FD1BFFFD04F87DFD5BFF27F8F8F827FD1BFFFD04F8 %A8FD5BFF52F8F8F852FD1BFFFD04F87DFD5BFF52F8F8F827FD1BFFFD04F8 %7DFD5BFF52F8F8F852FD1BFFFD04F87DFD5BFF52F8F8F827FD1BFFFD04F8 %7DFD5BFF52F8F8F852FD1BFFFD04F87DFD5BFF52F8F8F827FD1BFFFD04F8 %7DFD1AFFA8FFA8FD3EFF52F8F8F852FD1BFFFD04F87DFD11FFA8A8525227 %27F827FD05F82752FD3BFF27F8F8F827FD1BFF27F8F8F87DFD0EFF7DA852 %27FD0EF827FD3AFF52F8F8F852FD1BFFFD04F852FD0CFF7DFD14F827FD0D %FF7DFD0B52277DFD04FFFD1052277DFD08FF52F8F8F827FD1BFF27F8F8F8 %7DFD0BFFA8FD16F87DFD0CFF7DFD0CF827FFFFFFA8FD11F852FD08FF52F8 %F8F852FD1BFFFD04F852FD0BFF52FD0AF8FD0827FD04F827A8FD0BFF52FD %0CF827FD04FFFD11F852FD08FF52F8F8F827FD1BFF27F8F8F87DFD0BFF52 %FD05F8272752527D527D527D5252527D27FD04F852FD0BFF7DFD0CF827FD %04FFFD11F852FD08FF52F8F8F852FD1BFFFD04F852FD0BFF27FD04F8FD0F %52FD05F8A8FD0AFF7D27275227272752272727522752FD04FFFD05275227 %272752272727522727277DFD08FF52F8F8F827FD1BFF27F8F8F87DFD0BFF %27F8F8F827FD1052FD04F852FD36FF52F8F8F852FD1BFFFD04F852FD0BFF %FD04F82752525227525252275252522752525227FD04F87DFD36FF27F8F8 %F827FD1BFF27F8F8F87DFD0BFF27F8F8F8FD1152FD04F87DFD36FF52F8F8 %F852FD1BFFFD04F852FD0BFFFD04F827FD0F5227FD04F8A8FD36FF52F8F8 %F827FD1BFF27F8F8F87DFD0BFF27F8F8F827FD1052FD04F87DFD36FF52F8 %F8F852FD1BFFFD04F852FD0BFF52FD04F8FD05522752525227FD0552FD05 %F87DFD36FF52F8F8F827FD1BFF27F8F8F87DFD0BFFA8FD04F8277DFD0C52 %FD06F8A8FD36FF52F8F8F852FD1BFFFD04F852FD0BFFA8FD05F827FD0952 %27FD07F852FD37FF52F8F8F827FD1BFF27F8F8F87DFD0CFF52FD05F827FD %085227FD06F852FD0CFFA8527D527D527D527D527D527D527D527D527D52 %7D527D527D527DFD11FF52F8F8F852FD1BFFFD04F852FD0DFFFD07F85252 %5227525252FD06F87DFD0DFF52FD1AF8FD11FF27F8F8F827FD1BFF27F8F8 %F87DFD0EFF27FD05F827FD065227F8F8F852A8FD0EFF7DFD19F827FD11FF %52F8F8F852FD1BFFFD04F852FD0FFF27FD05F827FD0552FD04F87DFD0FFF %52FD1AF8FD11FF52F8F8F827FD1BFF27F8F8F87DFD10FF52FD04F827FD05 %52FD04F87DFD0FFF7DF8FD1727F852FD11FF52F8F8F852FD1BFFFD04F852 %FD11FF27FD04F827FD0452FD04F87DFD11FFA8FFFFFFA8FFFFFFA8FFFFFF %A8FFFFFFA8FFFFFFA8FFFFFFA8FD11FF52F8F8F827FD1BFF27F8F8F87DFD %12FFFD05F8FD045227F8F8F87DFD3BFF52F8F8F852FD1BFFFD04F852FD12 %FF7DFD04F827525252FD04F827FD3BFF52F8F8F827FD1BFF27F8F8F87DFD %12FFA8FD04F8FD045227FD04F8A8FD3AFF52F8F8F852FD1BFFFD04F852FD %12FFA8FD04F82752525227FD04F852FD3AFF27F8F8F827FD1BFF27F8F8F8 %7DFD12FFA8FD04F8FD04527DFD05F8A8FD39FF52F8F8F852FD1BFFFD04F8 %52FD12FF7DFD04F827FD0552FD04F827FD39FF52F8F8F827FD1BFF27F8F8 %F87DFD12FF52FD04F8FD065227FD04F8A8FD38FF52F8F8F852FD1BFFFD04 %F852FD11FFA827F8F8F82752522752525227FD04F827FD38FF52F8F8F827 %FD1BFF27F8F8F87DFD12FFFD04F8277DFD05527D27FD04F8A8FD37FF52F8 %F8F852FD1BFFFD04F852FD11FF7DFD04F8FD0952FD05F8FD37FF52F8F8F8 %27FD1BFF27F8F8F87DFD11FF7DFD04F827FD085227FD04F852FD36FF52F8 %F8F852FD1BFFFD04F852FD0FFF7D27FD05F827FD04522752525227FD05F8 %52FD09FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FD0DFF27F8F8F827FD1BFF27F8F8F87DFD0CFFA87D27FD07F8 %277DFD07527D27FD05F87DFD08FF7DF827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F8F8A8FD0CFF52F8F8F852FD1BFFFD04F8 %52FD0AFFA827FD09F827FD0B52FD05F827FD08FF52FD1EF8A8FD0CFF52F8 %F8F827FD1BFF27F8F8F87DFD09FFA8FD09F827527DFD0C52FD04F827FD08 %FF7DFD1EF8A8FD0CFF52F8F8F852FD1BFFFD04F852FD09FFFD09F8525252 %275252522752525227FD045227F8F8F852FD08FF52FD1EF8A8FD0CFF52F8 %F8F827FD1BFF27F8F8F852FD08FFA8FD06F827277DFD0F527D27F8F8F827 %FD08FFA87DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA8 %FD047DA8FD0CFF52F8F8F852FD1BFF27F8F8F852FD08FFA8FD04F827FD14 %5227F8F8F852FD34FF52F8F8F827FD1BFF52F8F8F852FD09FFFD04F8FD15 %5227F8F8F827FD34FF52F8F8F852FD1BFF27F8F8F827FD09FFFD04F827FD %055227FD0E5227F8F8F827FD34FF27F8F8F827FD1BFF52F8F8F852FD09FF %27F8F8F8FD0527F8272752275227522752275227522752FD04F827FD09FF %7DA87DA8A8A87DA8A8A87DA8A8FD1EFF52F8F8F852FD1BFF27F8F8F852FD %09FFFD1DF827FD08FF52FD0CF827FD1EFF52F8F8F827FD1BFF52F8F8F852 %FD08FFA827FD1CF827FD08FF7DFD0CF827FD1EFF52F8F8F852FD1BFF27F8 %F8F852FD08FFA8FD1DF827FD08FF52FD0CF827FD1EFF52F8F8F827FD1BFF %52F8F8F852FD08FF7DFD04F8FD0527F8F8F827F8F8F827F8F8F827F8F8F8 %27FD04F827FD08FF7DF827F8F8F827F8F8F827F8F827FD1EFF52F8F8F852 %FD1BFF27F8F8F852FD08FFA87DA8A8FFA8FFFFFFFD15A8FD09FFFD0EA8FD %1EFF52F8F8F827FD1BFF52F8F8F852FD5BFF52F8F8F852FD1BFF27F8F8F8 %27FD5BFF27F8F8F827FD1BFF52F8F8F852FD5BFF52F8F8F852FD1BFF27F8 %F8F852FD5BFF52F8F8F827FD1BFF52F8F8F852FD5BFF52F8F8F852FD1BFF %27F8F8F852FD5BFF52F8F8F827FD1BFF52F8F8F852FD5BFF52F8F8F852FD %1BFF27F8F8F852FD5BFF52F8F8F827FD1BFF52F8F8F852FD5BFF52F8F8F8 %52FD1BFF27F8F8F827FD5BFF27F8F8F827FD1BFF52F8F8F852FD5BFF52F8 %F8F852FD1BFF27F8F8F852FD5BFF52F8F8F827FD1BFF27F8F8F827275227 %522752275227522752275227522752275227522752275227522752275227 %522752275227522752275227522752275227522752275227522752275227 %5227522752275227522752275227522752275227522752275227522727F8 %F8F852FD1BFFFD64F827FD1BFFFD64F852FD1BFFFD64F827FD1BFFFD6352 %277DFD9AFFFF %%EndData endstream endobj 106 0 obj <>stream -%AI12_CompressedDataxrG(?a]KočΑĄ&!cСH/7Z@@rSj(6 -ݵeVrf'H|n}5?a?~1$^}sj{}E_6wf/ໟ7r55,deFwWBWfۇ˫_g?%G:?ݿ]y>dkzN% -'56^l HOZw_׻7W] t|[An~^_ ޼p6^_^"u ޼Eb'_ -W@c;?B|AFsj} x;Z^a UEf?E6)P%ޔ]e7%4y 7R*d^PM-WT2rՌ\5#WU3rՌl5#*F90_ F`zX\[~ -;;smhloG?2_oAwU:.RkESKxXBD4Ŀ3gY>+fլMgr\j-B/"_pU^,j-R/2_R,euO,g\2$]^.НWn!m/Ec#6]%AQtcJ -r /+[f]4椢)a ̗YS |;sN5q Wì{- - ltg3UJ -\3/B3K*C5U`*CrTؿp7W_F0 cŻNv:鷚O j eE=[S-kXu^Z~3Q:-w^MVU WQ*t -`3~Y,ԥ,U+Z1/TuQ9\PEVHB(gC}\Y%`j52H@%s.`k=ŻԹ.Jk-uh)VPF4aNr̉9YŒu#y)h^pf`bV0185SUS s*I"s)3 3*ʜ.8\Y4w0B•%AΣE M -%ٜ%*ܩQSSДj?Q.\5YR* ?@hBʦ8ٌ &QhSV%}J<EgFpMԂ1 gU 8.1JpR@52L\0L)`<M<Zt4K;xr3?s?88ń've{)I?I/!4aHR;]?Y4ՎkZt^sIَkKDW*.A̎K,_"鸚Є[KE-F u%7,atb2x4$ :U XS밦-I["^Pª; !4n֡N|Ԙ\, "Y<" rNe uğ6Y,9Qe@4ԉ ]@2>#4R11 )q9 DBF "d")"<> YkEx$kY9$NRr'DSACEbdh2!C[zi@6s"`1rPZx)  1MzSγs ?S?+?<ڹUgvs)@\[ HCZ h - -j  5i smKX8Ԥ:̽ c0j +fPTX 4ke/+"E)<Kd\YaM2s+`Hte+IdRP9}DLɦ,Jꬱ Ib$jkOʭE)Uv%[7-!peJhW Ic-?2疏3KkYYySҚe1 -ZU刎?jPz^Ka9{ 3O/{qbRc_a効0zjWHTVnqRTXI#49^Nɪb8{GbQ8nx/2v&))5ie܊,NԹp1,s;H%ǥ3+/$^4)`e$:=c]mq+veIr*қt{p;: [TDf9YT ,,h:q2#!6Ts$hbORTE0QcK!9MS ȖrQ@c"V4[wGY#bi͇s|(!ogHic!2ah@ TAz#+n>n@`! W["K_qN"Ps\EJ|:'wSWooi~\eVzܱ?ZXH*#+J>K[Cؖ,مc{NY#Ƥ)Qgqc5W/#^VjI^DMԵ1D-y o,V$@gj/VwT#"+5[ rf`HO枟C+r|׈7U6V-I]v$" ;hCbaQaQUDTY9,H-$1aF7#d]L nhFNI -@bk (anAsjNztHi_ŷLɇj+vNcΩw9)y.8(GSʧ؋n>pIFuŲ֜bGXf űXOTE.tU^tOWE.tU^tAQ9_#URC^Bj BI׭s!KYUs mfͫ@*m/e[~M|0UW[ۆ!^ʇWnωqNVk^[g`B?ƀqQmچ_--+AgJfMjɬ`|]Nxu»:jb3ZБhtH#(5hM$XZj] MZP4"Xsg mk:Pu5zg:õ&es*"4M"6V8#3"W\6pwօ:b[9{ߙj\/) -rd$~6x~I3)D)TޛϿgˑF6Pu; ՄRg$VD+*brjTL\/(rA" -rDp+.hgl劖7XVaF-ق5ϼ$E81 ԖrUr3 IJ8EH]pD,g(T rLjpc[xaR WgEDB]]+$@!! oՏv~"kO+d;8[tZ%tXa'yUŝcY Vz^-UjbNS9wyS1g|Aʐ~[%gVHfA$-]!cUmhNgI}P1*HvGQDȝc'q&/")%d**s.hQx*;qYc E,?=Kx{jwp۸WuCѼ؛wK$H9/Q]4'^Rfn9~DnFq~-" 1\sۭ8zK٘Fl5P, zV-bwš/VtuƌP{_ѣ  ):⫹͕ۧ< -;}<+9G`݊"JFf^Zߴf4`̔\K6Lb74>[EzHaG=[&lDbr!ª -ko:R~Ɨ2hg,rwPX f1{[A2!vXY'$ b1r՜)3R9!|Ewt=J PwCcQ~E4.}hIii1S68Zk#eF>Ek"V~mcB2535ݓAiw3JBTZA\2Wt+ -]kQeFhThI>'8 5ϚFI+[ۜmCdӈkp:eBVP ,K/)!xZQ$:Y`͓£2>(ɨnC -[ܬm?H\:Dk@wh6v/H{8RF%O|5Ρ;3B`PqWyt?跑kc9دq~HL7󱽥'% y!M0%Q$D#s!Mepl`h.Sy3|%l"zDrR*x/%܊s:%I[XgQ}a_gfe=9ߍ3/39X)bq!$sYP+q 59YPD`3LmR|5meru lSRC'uZc I=%j:Y"[ޔɕ:~RR9RRgyP:P0ڭ2t.Q85"qqDG$b:p¡GHIho/|R$dzyˣ<'Q:.>EiB?qz.\ȭʇ1n;$vKy3o&tsh'='J.d*۝(ʩmN&!RX&U*ah7vuF-ޑhSϣ-ZYHɆ+B:E*#H7h6Jf֗O+bs -Z}v O+\Uv91%;Wj/@_Cl4G & };\ۊY ę}a7_χ4!.|pTy*a{2٪8NJ^?@{#&)Y959rs~ՂV9lKmT m}K8#ƻV=vü~KJM= Y0F(cߔh6יt- vit\ IrOY+۱(ޒ"wUyʴv;B$yZySюd'hejoꩼ*! y㰊Њ(BDf;C,DwCfhZkmX|Ő누0Q8%Z< -XEALq IbN!L6!&zr21QrH: L.@XtKlgѾvLYHCXk a8saÅſJ\5Yy"J#"\snls\Ph@gu;+xa;ejI;Wmr\lb҆O2jdmXN#ScM\zĞ+Z L421;=@YSF,D&\LWG8Rar,}>āܨ]. FCV*5nǭ,0n -bn}#@s |'?ĹLGlDz9杙ty`H> x+iށҫ5Eiil)UikFptlv8mvm793d;P#WU3rՌl5#WUcO*I|T Pj:t׉N*>y*'@mٺ۬מ9ܲcLx8)-lR*xb)չܨرsUI= -M]MJwYɺ2kP#,2t򬵞k&*3s֙U/CQQyz[*>0qGk (1jVQ3|0crd g \F᫺yF ?p%EV2j5Q՞yf5 $Obz"A"?vU~^+%k&:3v]79@Z@*Vm$=>U3"4>{\s z v5g=̺* JgFgTB3V\ܢ3C +>WoJ9F?"*w+CY5(Fyhヷޖ .KuEq/Hhנ0ptp]y7;wm6;oM7rľaդ=1+㚾8Φ^8wK'onEG4k1]ǵǵ4m [-!..|sMWrNJj+l:.:5iͼ anctS;cUlf#rX͊6Q{Jt%ƅfpMm~M,Ь@ӌ gF.IŬ+IsW{+a g~S -~73yil2A[oxQ8K;|`w.iFl04t^+d"9\h؜uմm[M+۳ߍ] -_{ 0ڙ݆"dwf]:mYami3J?v䴕"9.L7?8>@JZr^Kzkm.Ğ] wSv_{wQ!d|u܅mLs8aEm/v70x #Ûx˻S 70Q 0Vۧ!&(RtPLq`G-M1 7 ;EA(*)ƸHNDn]pKqi⪖(IY U @:euU#v}jFB_l#E~HBf '+52}c_7Oy:Z!-pM}"LSq:r&Gv.h.yMT8'4oARGS( \Z,=gR'\~ v>fŭDZ!<8vˮTK<,W$2Ϣs%H(a + 讈{8NB-mXZ'ߖ+Ǎ;^|ds[-JioQ}L -AQ K{;,;lgW;+r YWWw W#iXd㲢݉B(NGr\o!PZ'ϩh oZq @2vt%ldyb" h1&cf`=S}숔,V&K4 gȯG͗5V.Ce(e*~j [߰AúʸFmĜv.4[&s"LD 0ph`4Tkѩqz# ?sAM̝*b\B8k2,m;8rQP8 j66JCƔ)':4K;{YDI =݊ڌ!gIٲyEq0%m]F_zirbw-7X-#c.Uмp:E"p;Bܥƀ`㷋.ҿuPtsjIqAXPpjRfܹ fӆrVY zb§_r2/hfu9qnA[2ڥ;'ug?#KڒoH3EKf!g\E¸k]1*%47/8I#].)ZDmώUI]l m"@cC%&ƮJ]{c[~ KdaqT3;N  p(2̯',3vс<{fB$tfu:$g{M9&8Tj [(Y c[1nl6T6 +h68¡4b1Mpy0U3/L[UvъShh)qǪ쎣P=!ҳ'=zp9N5tqIOa5F:PLjc|vExM9v9L:jgd2;j5G4I]nEv[FEkh >`if!%>)_.aF2D3F r - iq ->}.8MT~]BwMۀ*%mç<"Yh_kf+rh=>b\] A,]J'92Mf|UJ Xiyt'o䫼;NLGy{gF7GY2Y#glűXi/7s؊cS88 lŬ/| -Y!l9pl$GF#Kb1T`{By+g2 3ⳏZ93u#5N@@/;WlNAb7 ԧU+l-he N,)壠˗Lg9O},j) |et L2/(yv`$ARu/*]bW+lи<#,oevőxqޔi^h(Ly=qk#¬(xf3;j Nӿӊ1s08.NJ b:;ta3R>V3 >j /p$E|d4[$K/LxՒڦ SN8ʸh$=td5;sg[J{UףV:}Kgȝ|8P*GjJ'␣ڞlyf*<(8rg8)8Nġ;kYt -sPQF]9) 9gǛ4BV%cPd -VyM )V4"ˏW6;%sUx)۰`j!ð`jVV!y0€ -* pdCS?ݧ<)wߙn$XB -ZsuC C8eE79ۍ<&ZN&9G묣k^2OKy&וZom `H s;+бZ%:O4Jr w-֥g8<hx*k/awÄH#qqL|$)1chnʹG ʼn#B|U(9%ZhEs¬ E7䢄T&ix4pB(*8STD'bQ=e8'K}YzO8܇'!yJO paSVԳ (}Q+rD? (&h!!a!ڧ+ LaC(2Gt>7I>zl,āƹI퓓\XkĀ!`;6%xKr|V!*5E a͓:#vu$ -w9Izӣ`Q0}@G3)$6L$*s{+TL炭.T7=WNcͻ<######|߹N3zw -ObE{,s8ǁ=e}f@fOge{,@!yD@!yD@OTPa@!Yu;3U爋,ZqVO\Z{o,m 9E{oǿɆ\ſŢb ׌~S̗M-NBڤfS3i匑3)QN 9 a t)S#!;c]̌&j;Wώ]d>쵳޶{ە "M~K>~{ܦYb6HU9t4bq#p -iL>h9 >Yo7G[0|!?E Xw9b.*ڦ]tu֕YMj$;%EqJjAؕ#mT2 AlWe7w@a/;J9 žbZzֳDܠUn҄e9L tjͦq%eZ[-n+Ĕr#rn@H ;$aPAÕ5G:cY%957|.JWQ*2g%95yOOg6"w[(3nZu1xhCU(jCQ``VQf  l٩H6X"1cXhxd7-V34e9:WK8!:sIl v;G$ qFI:J:owX+;72N75¦6ܭnv"Bj44;f>7_6^Icoɿ|EⳔM.sxӕk}lID9Y6,]M)o|K)6:E?nt.vb~4hG:vi} -I9$*ypm^e-}C1HsTJ< QyY' 8aC"=#ڑPxfTT36*t#:;p)FhiijN4vtN3<<3N;r#~[FɰъA$A˽G&^(64춧2 n΅ v47pD۱Xd&Uv~f6F_^%}=S#{OVJ&ȚNt}ӱcVԊ[SC4 cM-ˍ#'z:oO#Xvnu:0|N,i3 wZ b xXeȊbEp tᡞ.dmɯIU&XKSm*甑X91X2 A1 /# eU?UVAԵ(-LU8tmjLLZ U -.U==Ku3w#yLP'UL5a6uiɼ P*,g%zjfWmouAؿ$ave@@PPIۡ>ex.vH)}ݸ綕ȶWb lj"k qI`Wl⶛vNYMJm`i5ۉ׎kgvuҬN eޏ-Ŷb>ly m5(!%J~/1XH,4CI㮎]|D]PR˒hCw?iJ|w1d;6i"KAԶcЮD1]%ЎĶY߱DPb{QWĶ-%{.KZze]i-'Uats>6oٵ\k5qf,ƖCSc`W?xkat$] =5qS,YEj]OR*fvǶQh -?!Du+e+ -w#ƸgR qɋhTdp[z:]e]IHҤUUInںtY ~-u{2W|1p5)]d r,$J)4X`aRhqFq1u-⸒ž_j"ES?Yˮb_/GX)`;z=1-G1?΀"cb, m+(5^k&*fT2\iqnkT{n@zM1=@L.}_4Uܪ pøcZvLTa%%,=_{d݀efWeA*o~Jƶ`EI3;aѓ!:Z ƫa;Y%m KϒNb& rPRYq_IZ i/&;nA=鏭è8+0ѵٍ\ ʎu:qs &BȘDΓC== Ҏ4mCĕv;/vJL1Ե]c [-h+JvJnLa -B\ת5_KąGlTTk\H=hK^^j'+-iogc Z[sp!^ˆr$>|/(l(&L -HP9'8*Q~Q9*A#]>-@|_P-U ݠ -#T#]5AO׃Cogc Z[spnTz8i}MK@e] hj~ZN敊D+Ln-=S҈dt{Zsq-#IBf\U\~um -'VյkRƾ^حgȘke٘D^}9_\]_mow/+ї/n}>M/.n~ޯ/FO~ nvm;Vg:bn簒UX\&V,d|aA_T堨maCW>聇bUB )0Ym;rcf9sP>}BQiff)+FeWB#3_X ,#(*@*<. KThTUZ1S`nEMnQ`(̀Z -H _A xg%kP--U*mL9 Wsv7@[L(Rb`vNՙ􅕶jYz$SIEn(<rϖYU׶) +0\EԮMbB[5gq./H E\PZA J -jBk0L:/Cyu2sK]2 ,V:Ú\Ya 4BZ]w -K:0u\UWؘs*C6!n@w"3jė:߾n{_Sa?oo15kk9`5pb:|nC2\?^]@c>#/8zpꗇ{&/1Ʀz5__n_;`2twwGdzn`u2z5 屇AۛwW$W{VэNn};pHWP4`tx^n.6ܭ߾Юn.Q7KejF\SPquy[wH C>+٫`ӡy(Xlur\϶lg&>C=X_.6G_^߭An{#L4{U?/?{öAԽAѐNU% ޠ]#z7wַbu>C( / [u8R!6ҫa Kׯmx.Z25P>޳Q+~K5} δ^޿Tc{{ -Tam(-s(/ށe r_ׇz:_lw_ ;bb}@18KwۃMJ`k݃\v{9|ZQ ae?yz>a^_'>3O:|*\]ݿ,ӎHy緛_?h?c=5? =4zwD=!&oCzzLlf#X Sֳ=,~ f kς|1rR_}#`ͩ|حAҵ[/9;Я!DsfZYƕcfKwo}M5iBW@7p^=ijAOP_Nc) 7n/eWu>ґ] N f~A:0%~vA77rw<C݆5VBchm88ԇd:tgin7@)=L=ChDqt~2 OU5S~wd0L+ s/uBh`!v\%rz0L2- 0?3ėze?p~ɢC8CDgn~pqSa;3a#4#?pYO͉Fq Q~魧R~T -/z6zɟh:?Q(<1d3<>ِp^ -}B*ü_Ny=f}#Cƿ;Yݹ͆ Z;/B!uA`~/f~:PB H/(NB6w}{R T)Y/& WWW=sE~K>O~b~Um0r#a+=AC7ل3~e߂{nѹ tcG^bZ3}l Pr3jQ'3~ <\کI%oq];js -"Go,y8#n;N 9ϼ+𷇹9zs̝Я5X!@/ GJ9߿~nؠ˿ENQn|u ⷄXH6[(}Po *n7(ig/߬/ Wu YUW8A5@ѳA5TA5T#&fâz5).quyHӳ!?i~u -XLKMP{Q㧇_7Z:#ʏ4d }Y$(I6is 9~Ym0=ܻO)d76d;x-pgww۫5>%7Eۇa?8gX#{wu槫냇v}Ij0NB Gފe1<ѭrѳj+쐀K3v.Guv8c$z}q?ҥD-z+*f@^ 윲ws{YAd=axBq5YQ[ӷ!==&Ckz!فl{g7:ZA{9U~ ߠ ߠ__vA4kDTSDK},EN}Y]<ӭ]‹~8k됪?9<'cue&毃q}Saz}#.ھ]_\UnvpO;D"ѿDŮwb;|P+vχ7O|R {,,`;ٯ́QSȯp5[϶\][xqϷv5זp~0냵~0#lbsE<d>铷G[ }j^F>OBwۃϷmY'NR5 qK !v;x:bv1Cir "G1Wg5h;cldE~gz}ygPkAW3Bs$X"+ *u]FC& Գd"jHEt~gB%$} -m8߮?9}q4(;u`>uKz5h77ЕwhzA&=GS!Q{-v'*9jVBd)x>}/Ig -tIQE&SJM؀"|pPȌeKG1VDgRT9j3Y*3$3=+3QEYTU@M4`.%/TVYZᳪʵ U0iUa !$W2I=J0 _=Sڌd>uGRr~PR Oʴp%*{J`tVN.=ZuU/i`6歱hh&T4~_b{/IS<3hP4ژ WKl]nt0~/W5FXqfn"1v9cwMy7q/"; _=t3>o0 WAYFAf=bu-&•Jm4GFeʼ5^OfQIn8PJIQ.֓"etmbL Sd%Jp8OWO(TLLf刚*':7@@d- ~Y -G|ҏ0Y&C)'Po`@"eUл -Ǜ֣؎'UIGoOe2]HeSXRt'>=?{X ]nn1OV` -@l40叛5oJWzo./.o#(p% ϶oz%^JԣϿlLp}Go.%]*M(P~ïH I<=En`D_٧aQ/Df woB~n PyT˫ &_ARAȖ=M/.n~[+x Đ9rtXƍPt+ u=2\{giַכN$ ع1@":FGhGXHo 8R=1r{LTQFpYdu ADPiRg,$LPum_([Dr0"tfVT+}D?#̄z,t0BB=hRXD(1,`F*W7 -_-m_4d1:߈׹+'BΔs::F~e(-zjg;n@ܽx UDϢ|IN"}=D<8?xh_>$ i{hPwIKͨkzKm.6@}eTʘY4 ,I9QJ$iERfWfV]huRFQL"YhEQk/b[;.sfaȌ_J Ǎ:7Tᬘg,[?81u) BB0\觾$1Mg^`:evW@j49fgYύeÅ/u -\wuֿl<|wpÏ!3Ԟy: -ISN&QP֩—#Fޙ8.w?v_mW/%,g#JNq]-${so7pnA.]#q:%9]o @*4 -I }JE0MΡF T{{۽~#䔐Җ8PB}ݽoڽf%62o"@@>_2dkV(0gOhQmNե?ЕZ,%+C9V.a)AAD`J$a|B\C++j2c^oֺHO:)TT)^O66 K_WI0לz =5ʆ沪Jy7E7U?e*,u& )`ixEs$b&Islk.yY\DOem4ڵi_ b'Z Vb4Y H& :d@+.-tJ.L)m(Yw۱JhRU^պSHlצ'" SQ[k vCtewNWw*qV.u؄FI NTTqn3`ؕd֚F,dKsˣ5DmDO5ko5w-<ҁ+? &Xc=ۏ/aB]|dwǗ ``TجhǴ5GP4tYkv6dKcت[Z7/3[♕@!xWΦ r嬓g?Y*-6@)߸+B)*9Qҡ3 -3Ia5'Ǭ -!4f`"tSnY(~D =f&0HP9(c"BQ4ʩE1y.NI19# MN(ĤʲruڠJtV*& e1Tg >- eUAn8{*`ұb{CQQ}DMUp. 킂R0*Ŗ0#Ts#쨻􈱏!XKs-ZSEkn[/*Nw*hhFCY1aVp2mȿpiGG<Q.?@Io#wsE\h &> H2p|cܬO} W?w+ZWkA݈Jjk4Nǯ1f2hLϦh#j7#B7tT4n>>nֈqS5w -z(^xU=uԙ1pEնn=ZՠcziF[I98p XX̆IVKNHTBIH+`*xEP(iM](x:$3-tް,qH$f圎DСUfLMa;в -Z@+| JӀ$JYp*du*\IJ"51T\MxQJHҀiC/  *d01x-t'Q@Q -fvQ'Y3/fE'f4^t Q|HZ9R4JA% -`j/!!.MG zcLInOOY &LЯj@X'. l\)=U1*AU| -񂜒9/i+CiḊ2$-E3>hTv%. -4̓S0T\`Cc1"E)" {Z(ɇ|Ve,sxbU(@d, :J`]]\K -F}Ζ{d_ik88ȓϔ+8lo6lDMg]1ȪBJ`dnP -&]P6B0, W2: -Çj.bCAHgLEk1n]Eoz I(g)70Wl Mb-үdv)8\Y S_P@(Lz,,k<.'tay"DX"7D T(8*yx-"\g$C -`a|_ FUԣszⒷRLQ5'1@ŏZG,OYا;/+Gص vH@Y2k+iQfyHO=g9LJ]UM[B͆f&X,?`2B%;!Dт8DD P{+&t+BJTVLyX/+zm =/(pq4V}V+3s~ȂE@x -txXGzܥ@fp_3刐!#QQjv_7CE٨RӪގھwT̰fw C2^Y[CW s8T"tShT4te6ali@183ߨmЄܣ7.( -`5c/ zł>F+ #\ !]f#,Heh;2^=I>levF?c͜phEv@NT[` Vg{'U,tr7u]5 F0 ͖7F xtz6[2? d2K#7[ߏ D%z#V%0D[X4 -N8zV**Z>uJddxCU d4 6eE4`G19~d&\ y -:9C4 \y rmTTcNEP+ N'='|@$/dDe>2^:%ࡉp`5"> K! -B"d.ˑn{I0\<T#x<$3GD%e,4M#Hc[ 2FH\p/seE2&!P#(yLVL%x1p.dELEy,aY2ptax*vtbw - EDqAUYR!FpE$t73Ckݪ *,BX[0Z$CE8. LB j BY 9BCۨ$ETDIU_T ~ȧL7!upaW'2_g<ЏL a(*&PU^0'z8PMdAO& F_.pR^@,=H{B*}UqZ1;V*(`l7f$sJm"B!; 9VGT6A/,::`&,JG/i0 - -(Uc Hu'Orf5WDU (b D%n"*w"'6H;yg -.0zE&aSSrLDD81BeY: -LܰKeY -aݴ'B`Nd=4=):UOE. %(j謌^)tc PQVeԂz%BCbZAM7>0?%>єv?"`4 c"1IQ<*T`!,8ДL+R3EUa8Bh%n8!~>xI-Ԫ<+Ժ[QA$fLzC3 1bd R,z5 -;LSlP(9D٨x \]:X+4$2Փ-L3n@8eX58GCB1gb[ -5^ .SubQ8 -M"4GAnU~gPASNXKDLpd rX"w:)Udi)0ByAFO[j"̕,3 @F$\sfiC@G0MsB 1'Bh_^P, -qnbpx'faȘ P8$$tuXYU:2WY -05^  -de_5E8E}cn5_QQV9.^ϩ OQ_ܪ .Ҍ˂xyx2R/A -EY0QUY:ީ9(ӓ%hҴ66lF&RY8R@!4IW7F(4| -1 $ zfT ـ! ߐI̡pS }da/%C3 o,0l$UDA40r>p22:\J ̏Wix0 -bN$*"!J6-Y|wj(uCTDDuX*)ks :d[ٳdo+)ӆ  6pWO@`,Us訄q'447" 1,: -:H:2vR z( -f/# /a*f#<5X<zAVbAsI}8E90< E.f@,z7 o0TJQdBjwICg`aVijЀCAF9N^p<1DZ@A`t - BX|e"QYG.L -(9 -y$dae%4.X:xQQPgb様4THZ^F-L_Y]e^e?+L^%; \(+f.ß.F3_@xf+!l /(z*K /<Ⳮ" Y+ GKmwtccL1j62;kQxYۣ:Ym9b0(@>V]?ԪXzE8L3|Ke8ez9%A7 Y9 W1ͮ4Q;dJnA#˨>tȖ$IgeVDW=9^y3dFSHM -|qY$6QwR -2 ZBE* :+>h@gX! -{Y8^I }!v%tayH~PҐV[s~Z ȃFR 9g49,kQ|CN?v~zj=W%^Ď4:% M,ὍpRˠSHfN8ak]V` Wy0aF3m8zqE RQLaa26Wo]x +Dͯy-7ͭJ-<}\:wsSa$эׯNKpqY 4ec_ZZK/^"fa(ZXZQYXId.)aEY8"&& -/b!Qd)CO`L&p C! -C#T)(IJ8\ -CXtU޲ I0 - -+4bJ@k 1eӁ~p3yQ"z] 86RrV(4_@}@NO% AO3f8^+Z5 S$4 &r p1"<ԁtjY^7xDWxs`'뀷@3Cp<8I NaH j fȞ`%GZ"X YRh>-=^x(9BY━ E ŋOx+OBVF"*)/x'MfNNjhK  6ȢZ$HDHBȁ)(<(òr@,4,sCs; -͏&4?Ђ?LдA8\~ހ -blFC!u>S-SH&Ώvl>/hoORܙcb~svT֞-i$4";Ks[7fHWPmU?i7a֩Bfg2LqxH$+Y&*$h(mc;% -KRw4u# HC|m9RYcVRè[ RÀ(S4k>0 [A^K^W S6@f џ,R2\#(Jk;B^\Sm/ -'()UNSkv -'R2= : %ƒW}kܪ5WoаMZmM_>> f-8N 3XcωV9D8uO,w?GXTfJ%J%Ta!֥HDNlh ff,JMCHvZ0]BD$Sp欧z*1+n# UBH=fnhU(-# -lX|?a%L#`1Cl'#f DJcDﶔ*!ȸ4)C}fYuY-+Km7 lHQa#B7Z&ZiE158!pL*4n":.á=yAݤ9N,vGv` v<T-!5DLcCy pїX/Csm憀͍67;U"IQ_1zDbIE#P$7 zv oVJo87}_a3es0W(x1%WN"4 aC DsqP7ނĂ%ufؒD-xeC8y3j>4[34vhn0̏uv]DCtOѺwffc)CD︐k$]xSH3D]x9ϐp1NXQ4^I`t;<* - 2z9uL2¾BEe^~AjTޮ:/B:7SBSHl硸&՟t*&+|:F(Q8~)ux:R:u6jho.1]vdw [^Bqq$:J~%4a//,(6V'ee҉| g} --Gөj2 M8K٧cӂO4ɠt 8VcMSq'xLAޕ,yQW•mFJWl~g#0-bFKs-B^E~qM]7ɵ}Yjud+pmc_ -afa} PHRaV1nD]8yWt:,Xߘ'V@+mY9Wa*R<\$nbYb@j\ZXJu~G &YNd#V]D\\+EQj]nKE9re_D$]X4/#E[[?nYDss"Z5~ `"`_싀L"`_싀}ߕ)_싀}/{0L +~mM8b|tbC3̉׹/nB_\ v\h|;_ )k3/eYJ3Yf~?/!_fo'+@;oYV\p߀ YY&e#z8ԥ&e5ʿ۶?'d_3+̟ -x~}T`3xgX/~_!w6_,s,iڰ]tw~Af~@EgU\%phD -NjxEbJH>Xx~ p-S^HJǟGJF}m4QgQȬ\ pn:^ 8 ?@_4e}E' $HEdg!xE,s|>WT?eQ~ ͅ=zS"#կsr#(Rx˪F׏__A+Ef[\~c -/И_2%c$pW#_Q/`{ПT A2[[1SUU`Q z&d&FM=1U1FYKov\֚]Co`Rhuiz}!U:5s:+AhK3L6k#cܕ1**GKmm!8n~"kQ\w5D_\:3 ˭jcg^LMI*( :L|QNպNu0Pgx^ewFJihN?  -ZGGAQ6@s쎳-Q½c\ULM(B>O#┪ R=Nە`oȿhVx)df!H,.xο}Ԓ'r.$j'Ɏe5\.T LIuId#PqEio껔/ *@ր ˔BʄItg,Ulx#g5vCi2m#){jCsUl\Q@^TEoi\9ՈbxATöձ{]5mR Tj;. ~،,4{OkdLZzt gkP>ϏzS@MNʼntgJhW*Ki!Qa(ӯLZ@ -Pd*PjC^$9qD?Be8D /A%mDK@I Aqc(/tPRDv6`dCE~%1 T '"KEiP -U`dQgưf sHP7"۔xܝd*V RJ Yg)9QN"NDT?OSC0f~ ˔  Ȃ%ẘ%lh2`pM0@dEX-KN3Rs)P^Dʆf:wk((oc]k5Wz\`:4w1Rʛ8g -}3in$`pkِ8ھbTB-JW!@S\ ;A& s"@3Wp5@>4B. -OzA6aC;$c-kU2?Ȁ/e(2BQ}sQk:_O)":Y5rlT2hf#'"3O{ZN'!oN~OC/kK8^O -/8y?0n,`X?O_#G&V Ŀ> -QL6FZ_/3 6 ~;?qq`K #:돟dЋ.)l3.1{7=_ur0u;wpگ"c cuD)Nࣹzgb'hYswÉQ]K/9'[zVEL&􋠻1@ճ01/6+} ~l& -ǫfx "3Y]/h[4> b`8i+ՠ+{ZQ_ɿ>irQO*0ݰJJ?_8?M\Cg~YTp zM9ɵdj7\3'[Y -2 M47I[IQ]ZeN"/7Wh DߺTNu5:•X3.lU_WU +FX*&l=DXWxtGu\3:żyImP5 lީ8stZ2z`܆V+# -qڵ8s < -ccQԣOdg/kŴ욜F'3g`iU Ac#YP]'ڸ庨 U -ׅ6tFjRՉAp?wӉf?im/?hpx6qY DMR]UOrq.r,/ʹL)Ò^ڜ1_I]&ݵOlV]eMUkxƱgɐ&|>,iN6rbecލa;<3j=y5&3z m&zV & t20@cm65>yg

*erBr Y8ms ]kc*L(=~Htʷ+ ҄0GЎ -n\ܹ&.%R hWUq=~h:yNUes8GY *7̰} }y$kNEte]q:57k$CQzX$IE.\N*,Qjgak/:ε\1Wa*|\В utm.%8tm`2A6ʹkWje2E]$y,OkDD1Y)1`4>tyIu9\ct T$2Z"Y*3ُ lLî.@E,y"002*cmҕ50PX8HdE- CczT,iCsݜ眃+dPvaj>h F0M''O㾱?~ִF5}uOv[57EǥZfNun?g5r'4!ZfFj؜{"3ȚSJkUhR33r/d/:T2PT-e1~PKZ^T-ÚJR׬dONJ^>C[mÿ晕Gm MGNގgisl>0~8/Qoxg56=фlB(̄rIZU1n |#L3df6cӇ X"^'\amlce{aü4E,kVĜpn -p9w6\Y!?6M2+4d2{Qc4S'~}D~` UTԝ+d(E>Hur8bŠ7   -?j>XV!9K/i0hQ̡4,*9-iZz']pX5ӻ6˭2) kq\lmX9 Zctw3"W6 jdw~q'nƒn}W(JAiKJr0j1"hA+i(9;kJé6feT 'lLѪPjO떴QS3v'U7+笓~+A)}@ФAi*#LuF?٬d@6Hka1m3augK<7R 2u֬C"Wu'eWk8mbK˔NL1{\=uqVWڠǹ9ERm$BA3*(ױhfRQ{r]v] ìK\'^a$1\;Q xfy˼2[Sx';'\dÛ#$zx^L Z9ݎpxub'gr=.?ɼ=n7(Ki.#l]޺L9-llKVC_-^n1=&^0FsËʽJ&흥DR(ud]@!Mw;yFPg Iod%4#ju{+%.3dU$K[d'[Ӹ޼LMQuSjLGƂ2r$V'zMWZfUJ9%F֥wr}+Dog7OtNwNta o4s.b=WivRmV&d:ʙXU{p^Z-.[cw2hhX b>|"^rSٛ\{.R~'ϞR'tHaZ#nnx.X|+ŮI1v:xb3cQhg|ǫLsz0?rukwnT5Ng -t{ܖZK\S8f|=/^'ݑc<-'ύ^rcm}(%+'UPe ~>fw\'ݐ5zgt]$1wΫm Ek'<-76n|NTdY^a,cǗi&<"{[8 BsRbm櫾4;J=Fx~M6V0x,&vSsCJ0ϊ -sy╓7=b°VE[{L>V l=|_qlj{?}`oxuhI|) iRȝ6;Hûm*b(s= Pdd(/2fW)(tjC{M$Q,)p d.$ -LNЩ(6fM̂S|!-:Q8@I (se˾m.W. 1 ޙuXV8 1M=1lxk_r+Z"h,TY:љ1 ufnxMfidās4i.~T_k0Ʊq k0@"`DLA%pfm +ANVh_DqjP/$ / -~stn|OMc"VgFru%X{&_&s~yRID;94XYh&NؤY-^`2(T|!,8hSX(,)i~ڔ+|g焂 lx#gCM!s|.{I=kɋ~2Yz`1w~wh(K+ɳt2.]!(HaΙڄ 9ZHGˢc!y|}Nd}dN~|tѯ(~CxZs1Y|Ԏ3q9.96mޯVB^2da(Lj7ɳ\9~"fJ$" .dG[>ZML/iҎig^C9!vMG؞VN89tApR AO:=`<(Rב6eJ/N.,,)v4P#{y$xcp2;0Pݾg<ӹ hdDMxJ Ӿl_ɹO.۟p^諿Wz(u^JNuzMr@`!ȝikCKG9pw.փ{/??&+>NuND/5=T!k 0Se ߂tTR:oa&g| RQJݼ<=kTKpJԏ:tECT;jbUܧmitڰIEN|M678/5_gdyz3q)c¶R=2®{9% Fѳx BQz68 }P~V_ -^gc٤m۹Lc}77S`(g;D.h&0MMh6 -l7VJd -I1h%Fى6z>i|^x-.`u1vi&w X;9>N}~f?s!JwT^oQ)l9p4}C49rpKixG{ -<(8.|@&^GJ~<"7qf3s|6CE ,9$!"|ֆ*QsϓP.UqnJa ŠDXs"RHRy*̫kP/p4ҔNKŵduKýt4 J;^dj'ӎ}>ei"e9Mդ -o-$d !V+/\fcs"ql2~J ՉteWBJrܠPo|3%rB{WԿW<:u]&/)Ufqj{}L}_zqExC@,shdYysqt;aP#s@H{ -dXGsi_,5"d^漬#wπ{3,!-{3m |wLsDBNS -X+ix< ( v>|REsϻW{3s1۩d3lKۆSo1dO8^~f_9}2a(šv+I:jJuRVQR_Gdg#Ŧm_; #{M"_`9N 5PrS:|ʚԃo硣RC,=~jC+a`cs B8gXL6SòN1@мdӞӋ|~ϲ=*]k53"(m%[e -Ո=i -kTJUiIKs㮱gK4`wA^[F0Oqgc !ACנ"_kk,cѮtfV퓹&hڸUW$ib Hb9[0B?#V3>nдo]~?^Q{:0X7 SFt=2iISeAW׶ E.Fy#]!_hM4M} z?N Y"lT<%nn5'؆Y0nk+"`ĤEic/}\;L6v*h #u - ô}}Y@R<$k#Q&5jRqn1 وK6ZymjJ]˷Ç lQO=-;gͶ#E㖐ŵu>Ԫb/͈^Fs Vdr0[yLz\?s[fh#&w d֤f^bث0on[g3.b"~H*Ď g, ȕr+WOV3~gemZ q.*1YO ?27 ]jD@As^ՉViM{~m8 ǫ_Y dFO1mF]~%iS7EXYEr:M0V+FMz'ẕԈ(-;.gLʳ`J}(+nwvRxDhR执GO5 DmNVn-nw%vlo "g[^\bt^`ɏ 7MplmkO3S AILz-.SOr^o5J̙z^ﲩ/N玾}x6g|IjGdOum܃9{&_ -heĖy@Nl>u{whRوGïOL7;ԃ5v VA7|ys3I:C^0{Rd FfSߚ;9:v㓑l؛d+a8ꔳw\ -"͠ ˙ќD ólٝ&o=MoTH=mpHtTW#l#sq,Oݭ`*5Iv(ΚqgR''%k7nO =zϽ8|s3Q>887 -Ow'd].K}5}t vXps)λ]]ݍᖻوݭAuCwo=8 wd;tM0uKy|f0}lޗMٓކwS|;TNѽf|ݾo[ӱYx,t7gga{߼\U6oC\v?ozfm/I=qsXR -,<> kמPY:}|УcKI\y2Gg2;1O\Tb!ϭ'yTj:h;~zgKnm4wӯ[|kmޖ?v{ON8uo=DJ[=V丵yO|Nc^ Ni&ɠwfٛo'DZ,ɍy6ڒ6=Q|۝ƹ/ }{_ox}ywr*ݘ<_U{{_?sMCm:"7cx;z}NY]zl_id){8 o;sM/Ҟ?L\B/պq4,)FORǛVDR@.ぃq8όp9J_^@;^!޻ͼw¾{ώy98:;sTwnN[}w&` s0zY f^ft7 ^܌ͱ7v yûv\tw?4͎sݳس{Xw/|vW\6N(C^(J_AJ|C/'N1`$,jp5$Vz|y#~?EGO 6)lMvk,LDZWzwwdig'u )ˡKER,պA@O7Uȍ`G7]+C8xTi'TYS9|WCN6}6E#-5wu.s8 bHy~Fz>̔FÛV6~v+B2x+\Ӆ75?: -Lӣ +ǣ֕?ݏcwOWDuR lO~et[=s o)N$NO*m-uiRޟ^C,|e~s{lVS*+J`RU^'Ns?޻+'/GhJ|W'Uӗ}†:ڼSףf<{#>ݜћI6%f/kr箶w{]ewoplss̿>7j@~H{wamx{rqPOBU8.T[Ό&1Vݥn:zͳiL<^ʾxnh]Ѩ-<˽vH7T/k!Q}F*;a=WSVm~{rGmnz vdpt_ZcǫMq>Zyu9MlaԃeSkgƷ To"j&< &<=pnQ2Iejs蹈U3M6n~傱 1w(}A`O$;սjO/M" Oݓ;ĜpL$vEw:p. \#+Dca 2Zz;"$%q\9ܿwUvpdr\,;Ioӡ _QZ6]:![8[8 -dD25\qO"7|,|f5;zdme>8KGcKjf>OwĽSxaO[tku*ŝ1->ގ)fݓВNŖ}]ܩxsgJB=nq\\8SɳbK:}brӲ)&2ͻ}seaC]\ekzM0a/BU]6@:sTdRzNE<:0vjFG\+zoG&5 &Fi>-xhԨ/ -'(%f }| qh;?P(A8΢|<6zNnD(w3pc]NۓLJX]dAl 5 -A>}|]1k= :6{7l?^pu|gŴu_ܸIh=SFϿ~.JE(h5;fD/7 ?Ak96Z\?w\5ўZ4?P]¯&jJ&|OWӢE*@\RiQ_~^"*ǬϏ݇wnbv(]Ӟ8^ڄ_ۻJz'(~KU?|lXf{.izb$>eO1K!D|mN±iB5^DVdgo7]үw8imB|z"x2ǎB(V&<͵A\s9gzZOk}To_[cyvM^O`5ny5 0'|u.21a'̚?uR-5u G7&uU|ˀ9Ls X%(FO~lN"2gYcZ?j{߃NrN2 k͢|sfF&ś1XI(IfoYlzktEֿڨhb/Ց o( _FX c}&6 -ʒ?Q_ӴFbȦohY+y"b^6?3.^ >u'k*ӱ--ܹ9h*RV.7G& ڟot|+֡NJ1HTJEz5jEM5WaOl.7L`ն;d>W|Naϵb}f박~'O tw >؅&pGeu宑 t;Ʌ\SSR)}XgI!rh&w b9Жϛa38>_X!CAenTnxQ>s>#(xI$cY -RV ;ZO 7JyP:GfQĬaT="Qn㩳3P^0Пd#)yDMJmp>4.'ݞٱu+G'LbQM.ӻ[hr7w4 k:ުb_篱]й~0M_~Q$?Kp]s8PVn%#J G -][nd;ԁros=> <&tA -˲18M'~B0#ں6T-h]WjtBcY=VAqu/c¬S)0V3-|a`3j_㚸-Nyvn=d2i KW^\a1@2u0 S>հ 38 S ׺HI+CT23n(NDxeb! Vg [Q9ׯZXZʥulqAAݠjot6^*ׇIe9;(KXLaȤĝsge=|;-ۺaۤb?Mgꈼ]L1ٖxb#HgahC34&=-shW 85%0XAt*R%u-*XEXccVg-yՁAm,o39X"#V4f8؃NIYE,U/Pi1 - Qın{X6 ͗pbvɚ{Csɚ<$?'F p(wɂI}npPOI殜l۲ X  eXf}L{* Ӽt?'0;lT_gZ:i4L飍ٔLu21 |j6NP^!bZd U>4u~m~{jH)+F9͜1fh:- c!-+E`UDG>cY DӭOڙ: n3FECߊJ' s |_s}11$FKtJDE=MjT섎}O=c"6Qc|0dhqPV" 5| ۋ!v i1>s9Lԙa3k4fh9ǠѰ`«FiM9:51hT[h`pͱ:5w! WD JVj' &t7CM^-y *˟Ȃ-5N%35 )Y_f]f? 'q7 -5SRIa3TԦȻ=cНѽ& bUK{!_Z|b(p|߳zt nxȾ`!1:9wiYw1HNj}w1Dc㨨Wݐ" ¡9NZn% lW4 -0\RD$C61ҭF1LVH'"0i&9DOkw!%@=eޘje[3LJI,>_d{V|mn̤]!RG!: - l~[A>-HtHx1THeTHhӑׇ|i$XJ}= -cۆ¡|~jF6py:ν1N(ƫpxl@3 @2 IUeh6"D/H*Qdp Cb0Kthcv;/P-Okl=1ow=h %qsJ\V.>[wa=84Kr8ʬ#@7o T6X'jVfNa_e; -,{X, d#wV*_ꗗ~qw>}9 ai`q(׼vዿg#RF[Q3Xpf[U+ `( ƧMv`pt8_<> YfyBHMrGviԽrkh:Ncqڕ -Cfyz7pX:mt.}ɖ[48&#Lʶ+4ɶ.c^t&Rt8MGXm MVoM#Z2-1oMS6t6-mpЬm\Atz|)h/&r/kJsmFF Il׫ti <ۮWG.NDI!b2m %`(PϤNĪ -S9:2KD -Rv2.f<'+ӉH ~m -=sj,Z;et\1n˝2aܑ\l@: z~V\,Uf^*2wEed^s; fS-x{{ODvR~O55 ƺ>QX6n}B<vnIXIu7۪`\C̲sT}<ZV:[%@ZDEB rU{ @GX#w @& -w -T΅b+֭vVᎆ| B+mR-RWk&P<'vI -"irf"ZJ7F )rb>v1q6S:/DS9ʄPU,GY9oʾ#)N þp+0\H5 qɷӊt="Vۖ0Tmr62.1:T0PW%f3 *{Dt ")_fNɍ/\?L_yu}Ss,D* ׮w,2p*x7Իuʵ#2o&'aP:L=c 4EXVGl4yKŴFU$˜kM[E -ZAфrEqsȌӳth>Ξ_574 -ɍFQݷb47zM#xzGn5 ]|T_ M c}G_ɽ m`̃T5Ǹy(~bucLLP4P>qao&[ dM"v drj,&{`j[YCצ2 'NO"N2 hI - ugy*&CS⒏m=^Nǧz!qEB,YZfyaf,-_)Y$iƚU7֚)vkKc[7R~HT2k&Z h2 vtKågx&DzwW/fwjG9cƤLutTa[;#f 6-Hr"V =3]vѫ]Xťs{P)r',,nu1ʌbvUt"ft-iW1[, Kc.eDb!!Ę0t +Ovdo8td"kaPNl)'aF|0*zQ>k}Q\yEWz)ʧ*N剝Yd &W~76bt7~mn(R]?gyJnѫ -S׏Ÿĝ/>P.ٯmg+u\[rRŽĺ~|:b}@ U7upDڭaFbvQ׏~X׏.~t(uG]l~8&zm -o=cnF15uU8uLbr OԺ~׭WSWǺ~k"S]? [{\3Y[GTR=8 -s=>rrjU?X@Lv7Xs53#R38-U$Đ~*TVWs]?CXcѫyIX׏~~Bᚴ{0Oud}U?-gNӻL~D'Ou]?̼cois\顮pU\gQW~tP:zG;~AY1曬uFddX׏Ɇ\O?]KO|]?@Eqn +@.zRҍ|JvMEjR֘qoJn*j!s1\bWyn8vW&>`[9'C\?-H_x*Isљ?'s~bʵ?ΏM5E(+w.׹t r{O\}^O{㛳\椏 -h֞[ym8ʬO2^^6Y}:noB!+όiB/G{]gW0Ѹ+04&c}%7XYGa6p-iL.Q^Q;)B~Ia=.fΊlKԓM3td>]LC{5~w(T;GH>S}{PSay ou#e_oG)oed(wAwVhUW +o<ЯR:lHT0*-}=_t8LᑗjLdn"xzDOX!^c. Z뫫Auo =dU|Ӎ_;LS~?ǗӎFS$j0:vu>-XÛDs CzY[qw1'{sQ}nֿ׷O_:4?Y[z}Ohw?77Wߟ{ig}_;Sؑ7K7?Oͮt nG!ʑ F7Pc9Up"(lTS (1$Oނ?yZ҆#3Sh&.[xw`Vgol]ФC{\B8JbzҢAh~DMسzV&|)kJ]{AUY1(g(sՋ%;9'J?kڠLIzSR匬2ex^j_Þck /o|CMY8כlPLkh]pg)T^<ՙ3:RI\ZHdt}Íóry0-!T6{?x !Z*!jxߵf4|Ur$9jl[55Zt峻`{1KhJ-9ǜ/J~3ЯQ -e{#* y=R`>>hJua7wR@_tcp1Rak( xEGLX?4$#pY<*_d[r1A /z&W* ,0UG  l[1a20!ј{35ךȖ|Ya A~rl2YZtd-P PI co1>Z/謄 XZjB&\#0q%mui"mQIq^X3mqZcTDv XK!17'?2 d8ڋ+KhaJtJ+l>OoYǜ:8ZuF;$ sY6ڇׂM hg޾~Go{G"/L/ϼL}%*<{ Md-;{"4m~9Sl_^aVXȕ8o-^/젤kAuI9+{E;jO5mF?ܠbv6G~o ??)4o* $:2kx>YV7Y$`(޶|}ur}Y}gQf^, Q|]^.?;?T/fё3 -Ա8'm|\BwQi{hs8TrvF:g()w y?b"vj ߫-mz9z[ݭ-aw7++p͵G뒲ZлMV@ }+.tkYCg[.oIC)! k-rs#PZMd-ŁNh"2лE`R]@Ֆ ,BmK|ս?O'yՌWnc5/]4\Wl9%eNM(jE=)PLQQJV(2w~Y}^n-60:~y] ط^F^o}vb0 E:L^_Zz[5^.8kKjf%(pI4RuԇM \6^Or}o <:;oFaמO"5_y.@cK3.Q$dH -Ћ@(En| -9F/16n+G;J+rGN:y! Aa_ -my9zmj|S|JkOIn*S9S94c3Wk4t!n -S Z-f##~Hݜ9m k -m剃Ƙ?r(_-wOL!6ؼ].< -hIx( 9ԓ͛fbQ#q#ŋ(tF\sb? /EQ͒h?0SWy38dg٧*jRO{և{P`4a %H8Cpi*]E2 Ftf -4ǣ]G}H84”㸸D%n_І!Z /}KjhAfPg\cǠz,kL/e'a^\VbfC#K1qxCAYS:WƴpYߌT: ݄:" W&0p0j/?]Ջ@\xD\[>;yUV[YV%=+N_>kJ xzutWAA t}}AA]:;p t jW_1]:X]:8p t ]:Ugs t ±8p J@yU>6<`T\ z{zѢ9Ð,yZv_낓N)zZ_ŷc燍Xz V'g'K#?P⎐ˁ79iEc;]ax}_FGN:D;7ΗgM ߻=1|??:ԇ!5 A/#{`Cs |qE~! -9NOR( EY'+o ޜ_334wR|1 [yΕ -(J7/BOb|-`E`*Rtȋ9b)eGN)͋# -%""Xԩ\+fob0|" I=(qb>_Kx6!/Gt/dltUƗD`tQ \+ -;ٴPȋE^ʦ\>Dixsi,  ?* -A(䲐`Rq翾\4?*ρ.ԃ -| 0<#߬(W`shMpppy>`;4ϕy'B"b}p/ P>*K E|(wZgt!f}T,u_XV7XNy8}^, *|Nz4Gg~$k{(I=_^'HJK]s; p䀸[T8PH!R@_>&o?Vfz';j=_NvVOO&, endstream endobj 5 0 obj <> endobj 23 0 obj <> endobj 40 0 obj <> endobj 57 0 obj <> endobj 74 0 obj <> endobj 83 0 obj [/View/Design] endobj 84 0 obj <>>> endobj 66 0 obj [/View/Design] endobj 67 0 obj <>>> endobj 49 0 obj [/View/Design] endobj 50 0 obj <>>> endobj 32 0 obj [/View/Design] endobj 33 0 obj <>>> endobj 15 0 obj [/View/Design] endobj 16 0 obj <>>> endobj 92 0 obj [91 0 R] endobj 107 0 obj <> endobj xref 0 108 0000000004 65535 f -0000000016 00000 n -0000000215 00000 n -0000043300 00000 n -0000000006 00000 f -0000120314 00000 n -0000000008 00000 f -0000043351 00000 n -0000000009 00000 f -0000000010 00000 f -0000000011 00000 f -0000000012 00000 f -0000000013 00000 f -0000000014 00000 f -0000000017 00000 f -0000121132 00000 n -0000121163 00000 n -0000000018 00000 f -0000000019 00000 f -0000000020 00000 f -0000000021 00000 f -0000000022 00000 f -0000000024 00000 f -0000120384 00000 n -0000000025 00000 f -0000000026 00000 f -0000000027 00000 f -0000000028 00000 f -0000000029 00000 f -0000000030 00000 f -0000000031 00000 f -0000000034 00000 f -0000121016 00000 n -0000121047 00000 n -0000000035 00000 f -0000000036 00000 f -0000000037 00000 f -0000000038 00000 f -0000000039 00000 f -0000000041 00000 f -0000120455 00000 n -0000000042 00000 f -0000000043 00000 f -0000000044 00000 f -0000000045 00000 f -0000000046 00000 f -0000000047 00000 f -0000000048 00000 f -0000000051 00000 f -0000120900 00000 n -0000120931 00000 n -0000000052 00000 f -0000000053 00000 f -0000000054 00000 f -0000000055 00000 f -0000000056 00000 f -0000000058 00000 f -0000120526 00000 n -0000000059 00000 f -0000000060 00000 f -0000000061 00000 f -0000000062 00000 f -0000000063 00000 f -0000000064 00000 f -0000000065 00000 f -0000000068 00000 f -0000120784 00000 n -0000120815 00000 n -0000000069 00000 f -0000000070 00000 f -0000000071 00000 f -0000000072 00000 f -0000000073 00000 f -0000000000 00000 f -0000120597 00000 n -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000120668 00000 n -0000120699 00000 n -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000000000 00000 f -0000044986 00000 n -0000121248 00000 n -0000043697 00000 n -0000047975 00000 n -0000045290 00000 n -0000045177 00000 n -0000044267 00000 n -0000044425 00000 n -0000044473 00000 n -0000045059 00000 n -0000045091 00000 n -0000045326 00000 n -0000048050 00000 n -0000048228 00000 n -0000049218 00000 n -0000056284 00000 n -0000121273 00000 n -trailer <<6D1515C949CA44859614AB62569A4127>]>> startxref 121441 %%EOF \ No newline at end of file diff --git a/src/card.png b/src/card.png deleted file mode 100644 index caecc4d..0000000 Binary files a/src/card.png and /dev/null differ diff --git a/src/contactLogic.js b/src/contactLogic.js index b23be0b..f58839d 100644 --- a/src/contactLogic.js +++ b/src/contactLogic.js @@ -1,9 +1,9 @@ -// Logic for solid contacts - import * as UI from 'solid-ui' import * as $rdf from 'rdflib' import { store } from 'solid-logic' import { getPersonas } from './webidControl' +import * as debug from './debug' +import { getSameAs } from './localUtils' const ns = UI.ns const utils = UI.utils @@ -64,7 +64,7 @@ export async function saveNewContact (book, name, selectedGroups, klass) { try { await updater.updateMany([], agenda) // @@ in future, updater.updateMany } catch (e) { - console.error('Error: can\'t update ' + person + ' as new contact:' + e) + debug.error('Error: can\'t update ' + person + ' as new contact:' + e) throw new Error('Updating new contact: ' + e) } return person @@ -86,7 +86,7 @@ export async function saveNewGroup (book, name) { const gname = sanitizeToAlpha(name) const group = kb.sym(book.dir().uri + 'Group/' + gname + '.ttl#this') const doc = group.doc() - // console.log(' New group will be: ' + group + '\n') + // debug.log(' New group will be: ' + group + '\n') try { await kb.fetcher.load(gix) } catch (err) { @@ -124,12 +124,13 @@ export async function addPersonToGroup (thing, group) { try { await kb.fetcher.load(toBeFetched) } catch (e) { + //complain(dom, 'Error loading data for ' + thing + ' or ' + group + ': ' + e) throw new Error('addPersonToGroup: ' + e) } const types = kb.findTypeURIs(thing) // for (const ty in types) { - // console.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped? + // debug.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped? // } if (!(ns.vcard('Individual').uri in types || ns.vcard('Organization').uri in types)) { @@ -197,15 +198,10 @@ export function groupMembers (kb, group) { export function isLocal (group, item) { const tree = group.dir().dir().dir() const local = item.uri && item.uri.startsWith(tree.uri) - // console.log(` isLocal ${local} for ${item.uri} in group ${group} tree ${tree.uri}`) + // debug.log(` isLocal ${local} for ${item.uri} in group ${group} tree ${tree.uri}`) return local } -export function getSameAs (kb, item, doc) { - return kb.each(item, ns.owl('sameAs'), null, doc).concat( - kb.each(null, ns.owl('sameAs'), item, doc)) -} - export async function getDataModelIssues (groups) { const del = [] const ins = [] @@ -221,7 +217,7 @@ export async function getDataModelIssues (groups) { ins.push($rdf.st(group, ns.vcard('hasMember'), other, group.doc())) break } - // console.log('getDataModelIssues: ??? expected id not to be local ' + other) + // debug.log('getDataModelIssues: ??? expected id not to be local ' + other) } // other } // if }) // member diff --git a/src/contactsPane.js b/src/contactsPane.js index 452d093..e60c5c1 100644 --- a/src/contactsPane.js +++ b/src/contactsPane.js @@ -1,6 +1,6 @@ /* Contact AddressBook Pane ** -** This outline pane allows a user to interact with an contact, +** This outline pane allows a user to interact with a contact, to change its state according to an ontology, comment on it, etc. ** ** See also things like @@ -9,35 +9,42 @@ to change its state according to an ontology, comment on it, etc. ** http://www.iana.org/assignments/vcard-elements/vcard-elements.xhtml ** */ -/* global alert, confirm */ import { authn } from 'solid-logic' -import { addPersonToGroup, saveNewContact, saveNewGroup, groupMembers, getDataModelIssues } from './contactLogic' +import { saveNewContact, saveNewGroup } from './contactLogic' import * as UI from 'solid-ui' import { mintNewAddressBook } from './mintNewAddressBook' import { renderIndividual } from './individual' import { toolsPane } from './toolsPane' -import { groupMembership } from './groupMembershipControl' +import './styles/contactsPane.css' +import { + checkDataModel, ensureBookLoaded, renderGroupButtons, + refreshThingsSelected, refreshNames, selectAllGroups, loadAllGroups, + syncGroupUl, setActiveGroupButton, createGroupLi, refreshFilteredPeople, + deselectAllPeople, +} from './addressBookPresenter' +import { complain, deleteThingAndDoc, setDom } from './localUtils' +import * as debug from './debug' +import './styles/rdfFormsEnforced.css' const ns = UI.ns const utils = UI.utils -const style = UI.style export default { icon: UI.icons.iconBase + 'noun_99101.svg', // changed from embedded icon 2016-05-01 name: 'contact', - // Does the subject deserve an contact pane? + // Does the subject deserve a contact pane? label: function (subject, context) { const t = context.session.store.findTypeURIs(subject) if (t[ns.vcard('Individual').uri]) return 'Contact' - if (t[ns.vcard('Organization').uri]) return 'contact' + if (t[ns.vcard('Organization').uri]) return 'Contact' if (t[ns.foaf('Person').uri]) return 'Person' if (t[ns.schema('Person').uri]) return 'Person' if (t[ns.vcard('Group').uri]) return 'Group' if (t[ns.vcard('AddressBook').uri]) return 'Address book' - return null // No under other circumstances + return null // No, under other circumstances }, mintClass: UI.ns.vcard('AddressBook'), @@ -46,46 +53,33 @@ export default { // Render the pane render: function (subject, dataBrowserContext, paneOptions = {}) { - const thisPane = this - - // stick functions here - function complain (message) { - console.log('contactsPane: ' + message) - div.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) - } - function complainIfBad (ok, body) { - if (!ok) { - complain('Error: ' + body) - } - } - - // Reproduction: Spawn a new instance of this app + /* function newAddressBookButton (thisAddressBook) { return UI.login.newAppInstance( dom, { noun: 'address book', appPathSegment: 'contactorator.timbl.com' }, function (ws, newBase) { - thisPane.clone(thisAddressBook, newBase, { // @@ clone is not a thing - use mintNew + thisPane.mintNew(thisAddressBook, newBase, { me, div, dom }) } ) - } // newAddressBookButton + } */ const dom = dataBrowserContext.dom const kb = dataBrowserContext.session.store const div = dom.createElement('div') - const me = authn.currentUser() // If already logged on + setDom(dom) // set dom for ana error handling in other modules UI.aclControl.preventBrowserDropEvents(dom) // protect drag and drop div.setAttribute('class', 'contactPane') asyncRender().then( - () => console.log('contactsPane Rendered ' + subject), - err => complain('' + err)) + () => debug.log('contactsPane rendered ' + subject), + err => complain(div, dom, '' + err)) return div // Async part of render. Maybe API will later allow render to be async @@ -94,868 +88,716 @@ export default { const t = kb.findTypeURIs(subject) - let me = authn.currentUser() + // Render a single contact Individual + if ( + t[ns.vcard('Individual').uri] || + t[ns.foaf('Person').uri] || + t[ns.schema('Person').uri] || + t[ns.vcard('Organization').uri] || + t[ns.schema('Organization').uri] + ) { + renderIndividual(dom, div, subject, dataBrowserContext).then(() => debug.log('(individual rendered)')) + /* + // Render a Group instance + } + else if (t[ns.vcard('Group').uri]) { + // If we have a main address book, then render this group as a guest group within it + UI.login + .findAppInstances(context, ns.vcard('AddressBook')) + .then(function (context) { + const addressBooks = context.instances + const options = { foreignGroup: subject } + if (addressBooks.length > 0) { + // const book = addressBooks[0] + renderAddressBook(addressBooks, options) + } else { + renderAddressBook([], options) + // @@ button to Make a new addressBook + } + }) + .catch(function (e) { + complain(div, dom, '' + e) + }) + */ + // Render a AddressBook instance + } else if (t[ns.vcard('AddressBook').uri]) { + renderAddressBook([subject], {}) + } else { + debug.log( + 'Error: Contact pane: No evidence that ' + + subject + + ' is anything to do with contacts.' + ) + } - const context = { - target: subject, - me, - noun: 'address book', - div, - dom - } // missing: statusRegion + let me = authn.currentUser() - // Render a 3-column browser for an address book or a group - function renderThreeColumnBrowser (books, context, options) { + // Render AddressBook instance + function renderAddressBook (books, options) { kb.fetcher .load(books) .then(function (_xhr) { - renderThreeColumnBrowser2(books, context, options) + renderAddressBookDetails(books, options) }) .catch(function (err) { - complain(err) + complain(div, dom, '' + err) }) } - function renderThreeColumnBrowser2 (books, context, options) { + + function renderAddressBookDetails (books, options) { const classLabel = utils.label(ns.vcard('AddressBook')) - // const IndividualClassLabel = utils.label(ns.vcard('Individual')) - let book = books[0] // for now + let book = options.foreignGroup // in case we have only a Grouo + let title = '' + if (books && books.length > 0) { + book = books[0] // if we have an Address Book, we prefer this + title = utils.label(book.dir()) + } else { + kb.any(book, ns.dc('title')) || kb.any(book, ns.vcard('fn')) + if (paneOptions.solo && title && typeof document !== 'undefined') { + document.title = title.value // @@ only when the outermmost pane + } + title = title ? title.value : classLabel + } + const groupIndex = kb.any(book, ns.vcard('groupIndex')) - let selectedGroups = {} + const selectedGroups = {} let selectedPeople = {} // Actually prob max 1 - const target = options.foreignGroup || book - - let title = - kb.any(target, ns.dc('title')) || kb.any(target, ns.vcard('fn')) - if (paneOptions.solo && title && typeof document !== 'undefined') { - document.title = title.value // @@ only when the outermmost pane - } - title = title ? title.value : classLabel + let allGroupsLi = null + let newGroupLi = null - // The book could be the main subject, or linked from a group we are dealing with - function findBookFromGroups (book) { - if (book) { - return book - } - let g - for (const gu in selectedGroups) { - g = kb.sym(gu) - const b = kb.any(undefined, ns.vcard('includesGroup'), g) - if (b) return b + // Centralized active-button tracking across all action buttons + const actionButtons = [] + function setActiveActionButton (activeBtn) { + actionButtons.forEach(btn => { + btn.classList.remove('btn-primary') + btn.classList.add('btn-secondary') + }) + if (activeBtn) { + activeBtn.classList.remove('btn-secondary') + activeBtn.classList.add('btn-primary') } - throw new Error( - 'findBookFromGroups: Cant find address book which this group is part of' - ) } - // Write a new contact to the web - - // organization-name is a hack for Mac records with no FN which is mandatory. - function nameFor (x) { - const name = - kb.any(x, ns.vcard('fn')) || - kb.any(x, ns.foaf('name')) || - kb.any(x, ns.vcard('organization-name')) - return name ? name.value : '???' + // Shared context passed to all builder functions + const ctx = { + dom, kb, ns, book, options, title, groupIndex, + selectedGroups, get selectedPeople () { return selectedPeople }, + set selectedPeople (v) { selectedPeople = v }, + get allGroupsLi () { return allGroupsLi }, + set allGroupsLi (v) { allGroupsLi = v }, + get newGroupLi () { return newGroupLi }, + set newGroupLi (v) { newGroupLi = v }, + actionButtons, setActiveActionButton, + dataBrowserContext, div, me, + setMe (v) { me = v }, + paneOptions, } - function filterName (name) { - const filter = searchInput.value.trim().toLowerCase() - if (filter.length === 0) return true - const parts = filter.split(' ') // Each name part must be somewhere - for (let j = 0; j < parts.length; j++) { - const word = parts[j] - if (name.toLowerCase().indexOf(word) < 0) return false - } - return true - } + // ── Build layout ──────────────────────────────────────────── + const { main, addressBookSection, detailsSection } = buildMainLayout(ctx) + div.appendChild(main) - function selectPerson (person) { - cardMain.innerHTML = 'loading...' - selectedPeople = {} - selectedPeople[person.uri] = true - refreshFilteredPeople() // Color to remember which one you picked - const local = book ? localNode(person) : person - kb.fetcher.nowOrWhenFetched(local.doc(), undefined, function ( - ok, - message - ) { - cardMain.innerHTML = '' - if (!ok) { - return complainIfBad( - ok, - 'Can\'t load card: ' + local + ': ' + message - ) - } - // console.log("Loaded card " + local + '\n') - cardMain.appendChild(renderPane(dom, local, 'contact')) - cardMain.appendChild(dom.createElement('br')) - - cardMain.appendChild(UI.widgets.linkIcon(dom, local)) // hoverHide - - // Add in a delete button to delete from AB - const deleteButton = UI.widgets.deleteButtonWithCheck( - dom, - cardMain, - 'contact', - async function () { - const container = person.dir() // ASSUMPTION THAT CARD IS IN ITS OWN DIRECTORY - // alert('Container to delete is ' + container) - const pname = kb.any(person, ns.vcard('fn')) - if ( - confirm( - 'Delete contact ' + pname + ' completely?? ' + container - ) - ) { - console.log('Deleting a contact ' + pname) - await loadAllGroups() // need to wait for all groups to be loaded in case they have a link to this person - // load people.ttl - const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) - await kb.fetcher.load(nameEmailIndex) - - // - delete person's WebID's in each Group - // - delete the references to it in group files and save them back - // - delete the reference in people.ttl and save it back - - // find all Groups - const groups = groupMembership(person) - let removeFromGroups = [] - // find person WebID's - groups.forEach(group => { - const webids = getSameAs(kb, person, group.doc()) - // for each check in each Group that it is not used by an other person then delete - webids.forEach(webid => { - if (getSameAs(kb, webid, group.doc()).length === 1) { - removeFromGroups = removeFromGroups.concat(kb.statementsMatching(group, ns.vcard('hasMember'), webid, group.doc())) - } - }) - }) - // console.log(removeFromGroups) - await kb.updater.updateMany(removeFromGroups) - await deleteThingAndDoc(person) - await deleteRecursive(kb, container) - refreshNames() // "Doesn't work" -- maybe does now with waiting for async - cardMain.innerHTML = 'Contact Data Deleted.' - } - } - ) - deleteButton.style = 'height: 2em;' - }) + function showDetailsSection () { + detailsSection.classList.remove('hidden') } + ctx.showDetailsSection = showDetailsSection + ctx.detailsSection = detailsSection + + // Create shared DOM elements needed by multiple builders + const ulPeople = dom.createElement('ul') + ulPeople.setAttribute('role', 'list') + ulPeople.setAttribute('aria-label', 'People list') + ctx.ulPeople = ulPeople + + const detailsSectionContent = dom.createElement('div') + detailsSectionContent.classList.add('detailsSectionContent') + detailsSectionContent.setAttribute('role', 'region') + detailsSectionContent.setAttribute('aria-labelledby', 'detailsSectionContent') + detailsSectionContent.setAttribute('aria-live', 'polite') + ctx.detailsSectionContent = detailsSectionContent + + // ── Header (title + New Contact button) ───────────────────── + const headerSection = buildHeaderSection(ctx) + addressBookSection.appendChild(headerSection) + + const dottedHr = dom.createElement('hr') + dottedHr.classList.add('dottedHr') + addressBookSection.appendChild(dottedHr) + + // ── Search ────────────────────────────────────────────────── + const { searchSection, searchInput } = buildSearchSection(ctx) + ctx.searchInput = searchInput + addressBookSection.appendChild(searchSection) + + // ── Group bar ─────────────────────────────────────────────── + const { buttonSection, ulGroups } = buildGroupBar(ctx) + ctx.ulGroups = ulGroups + addressBookSection.appendChild(buttonSection) + + // ── People list ───────────────────────────────────────────── + const peopleListSection = dom.createElement('section') + peopleListSection.classList.add('peopleSection') + addressBookSection.appendChild(peopleListSection) + peopleListSection.appendChild(ulPeople) + + // ── Details content section ───────────────────────────────── + detailsSection.appendChild(detailsSectionContent) + + // ── Footer buttons ────────────────────────────────────────── + const cardFooter = buildFooterButtons(ctx) + addressBookSection.appendChild(cardFooter) + + checkDataModel(book, detailsSectionContent).then(() => { debug.log('async checkDataModel done.') }) + } - function refreshFilteredPeople (active) { - let count = 0 - let lastRow = null - for (let i = 0; i < peopleMainTable.children.length; i++) { - const row = peopleMainTable.children[i] - const matches = filterName(nameFor(row.subject)) - if (matches) { - count++ - lastRow = row - } - row.setAttribute( - 'style', - matches - ? selectedPeople[row.subject.uri] - ? 'background-color: #cce;' - : '' - : 'display: none;' - ) - } - if (count === 1 && active) { - const unique = lastRow.subject - // selectedPeople = { } - // selectedPeople[unique.uri] = true - // lastRow.setAttribute('style', 'background-color: #cce;') - selectPerson(unique) - } - } + // /////////////// Fix user when testing on a plane - function selectAllGroups ( - selectedGroups, - groupsMainTable, - callbackFunction - ) { - function fetchGroupAndSelct (group, groupRow) { - groupRow.setAttribute('style', 'background-color: #ffe;') - kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function ( - ok, - message - ) { - if (!ok) { - const msg = 'Can\'t load group file: ' + group + ': ' + message - badness.push(msg) - return complainIfBad(ok, msg) - } - groupRow.setAttribute('style', 'background-color: #cce;') - selectedGroups[group.uri] = true - refreshGroupsSelected() - refreshNames() // @@ every time?? - todo -= 1 - if (!todo) { - if (callbackFunction) { callbackFunction(badness.length === 0, badness) } - } - }) - } - let todo = groupsMainTable.children.length - var badness = [] /* eslint-disable-line no-var */ - for (let k = 0; k < groupsMainTable.children.length; k++) { - const groupRow = groupsMainTable.children[k] - const group = groupRow.subject - fetchGroupAndSelct(group, groupRow) - } // for each row - } + if ( + typeof document !== 'undefined' && + document.location && + ('' + document.location).slice(0, 16) === 'http://localhost' + ) { + me = kb.any(subject, UI.ns.acl('owner')) // when testing on plane with no webid + debug.log('Assuming user is ' + me) + } - async function loadAllGroups () { - await kb.fetcher.load(groupIndex) - const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : [] - await kb.fetcher.load(gs) - return gs - } + return div + } // asyncRender + } // render function +} // pane object - function groupsInOrder () { - let sortMe = [] - if (options.foreignGroup) { - sortMe.push([ - '', - kb.any(options.foreignGroup, ns.vcard('fn')), - options.foreignGroup - ]) - } - if (book) { - books.forEach(function (book) { - const gs = book ? kb.each(book, ns.vcard('includesGroup'), null, groupIndex) : [] - const gs2 = gs.map(function (g) { - return [book, kb.any(g, ns.vcard('fn')), g] - }) - sortMe = sortMe.concat(gs2) - }) - sortMe.sort() - } - return sortMe.map(tuple => tuple[2]) - } +// ── Helper: handle "New group" button click ────────────────────────── +async function handleNewGroupClick (ctx) { + const { dom, kb, ns, book, options, selectedGroups, dataBrowserContext, div } = ctx + ctx.showDetailsSection() + ctx.detailsSectionContent.innerHTML = '' + const groupIndex = kb.any(book, ns.vcard('groupIndex')) + try { + await kb.fetcher.load(groupIndex) + } catch (e) { + debug.log('Error: Group index NOT loaded:' + e + '\n') + } + debug.log(' Group index has been loaded\n') + + const name = await UI.widgets.askName( + dom, kb, ctx.detailsSectionContent, UI.ns.foaf('name'), ns.vcard('Group'), 'group') + if (!name) return // cancelled by user + let group + try { + group = await saveNewGroup(book, name) + } catch (err) { + debug.log('Error: can\'t save new group:' + err) + ctx.detailsSectionContent.innerHTML = 'Failed to save group' + err + return + } + for (const key in selectedGroups) delete selectedGroups[key] + selectedGroups[group.uri] = true + + // Refresh the group buttons list + const allGroupsLi = ctx.allGroupsLi + const newGroupLi = ctx.newGroupLi + if (allGroupsLi.parentNode) allGroupsLi.parentNode.removeChild(allGroupsLi) + if (newGroupLi.parentNode) newGroupLi.parentNode.removeChild(newGroupLi) + syncGroupUl(book, options, ctx.ulGroups, dom, selectedGroups, ctx.ulPeople, ctx.searchInput) + ctx.ulGroups.insertBefore(allGroupsLi, ctx.ulGroups.firstChild) + ctx.ulGroups.appendChild(newGroupLi) + refreshThingsSelected(ctx.ulGroups, selectedGroups) + // Highlight the new group button in ulGroups and show empty people list + const matchingLi = Array.from(ctx.ulGroups.children).find(li => li.subject && li.subject.uri === group.uri) + setActiveGroupButton(ctx.ulGroups, matchingLi ? matchingLi.querySelector('button') : null) + refreshNames(ctx.ulPeople, null, false) + + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSectionContent.appendChild(UI.aclControl.ACLControlBox5( + group.doc(), dataBrowserContext, 'group', kb, + function (ok, body) { + if (!ok) { + ctx.detailsSectionContent.innerHTML = + 'Group sharing setup failed: ' + body + } + })) +} - function renderPane (dom, subject, paneName) { - const p = dataBrowserContext.session.paneRegistry.byName(paneName) - const d = p.render(subject, dataBrowserContext) - d.setAttribute( - 'style', - 'border: 0.1em solid #444; border-radius: 0.5em' - ) - return d - } +// ── Helper: render askName form for person or organization ─────────── +function createNewPersonOrOrganization (ctx, formContainer, klass) { + const { dom, kb, ns, book, selectedGroups, dataBrowserContext } = ctx + formContainer.innerHTML = '' + UI.widgets + .askName(dom, kb, formContainer, UI.ns.foaf('name'), klass) + .then(async (name) => { + if (!name) return // cancelled by user + ctx.detailsSectionContent.innerHTML = 'Indexing...' + let person + try { + person = await saveNewContact(book, name, selectedGroups, klass) + } catch (err) { + const msg = 'Error: can\'t save new contact: ' + err + debug.log(msg) + alert(msg) + return + } + ctx.selectedPeople = {} + ctx.selectedPeople[person.uri] = true + refreshNames(ctx.ulPeople, null) // Add name to list of group + ctx.detailsSectionContent.innerHTML = '' // Clear 'indexing' + ctx.detailsSectionContent.classList.add('detailsSectionContent--wide') + const contactPane = dataBrowserContext.session.paneRegistry.byName('contact') + const paneDiv = contactPane.render(person, dataBrowserContext) + paneDiv.classList.add('renderPane') + ctx.detailsSectionContent.appendChild(paneDiv) + }) +} - function compareForSort (self, other) { - let s = nameFor(self) - let o = nameFor(other) - if (s && o) { - s = s.toLowerCase() - o = o.toLowerCase() - if (s > o) return 1 - if (s < o) return -1 - } - if (self.uri > other.uri) return 1 - if (self.uri < other.uri) return -1 - return 0 - } +// ── Builder: main layout skeleton ──────────────────────────────────── +function buildMainLayout (ctx) { + const { dom } = ctx + const main = dom.createElement('main') + main.id = 'main-content' + main.classList.add('addressBook-grid') + main.setAttribute('role', 'main') + main.setAttribute('aria-label', 'Address Book') + main.setAttribute('tabindex', '-1') + + const addressBookSection = dom.createElement('section') + addressBookSection.setAttribute('aria-labelledby', 'addressBook-section') + addressBookSection.classList.add('addressBookSection', 'section-bg') + addressBookSection.setAttribute('role', 'region') + addressBookSection.setAttribute('tabindex', '-1') + main.appendChild(addressBookSection) + + const detailsSection = dom.createElement('section') + detailsSection.classList.add('detailSection') + detailsSection.setAttribute('role', 'region') + detailsSection.setAttribute('aria-label', 'Details section') + detailsSection.classList.add('hidden') + main.appendChild(detailsSection) + + return { main, addressBookSection, detailsSection } +} - // In a LDP work, deletes the whole document describing a thing - // plus patch out ALL mentiosn of it! Use with care! - // beware of other data picked up from other places being smushed - // together and then deleted. - - async function deleteThingAndDoc (x) { - console.log('deleteThingAndDoc: ' + x) - const ds = kb - .statementsMatching(x) - .concat(kb.statementsMatching(undefined, undefined, x)) - try { - await kb.updater.updateMany(ds) - console.log('Deleting resoure ' + x.doc()) - await kb.fetcher.delete(x.doc()) - console.log('Delete thing ' + x + ': complete.') - } catch (err) { - complain('Error deleting thing ' + x + ': ' + err) - } - } +// ── Builder: header with title and New Contact button ──────────────── +function buildHeaderSection (ctx) { + const { dom, ns, kb, title, me, setMe, setActiveActionButton, paneOptions } = ctx + + const headerSection = dom.createElement('section') + headerSection.classList.add('headerSection') + + const header = dom.createElement('header') + header.classList.add('mb-md') + const h2 = dom.createElement('h2') + h2.id = 'addressBook-heading' + h2.setAttribute('tabindex', '-1') + h2.textContent = title + + // New Contact button + const newContactButton = dom.createElement('button') + const container = dom.createElement('div') + newContactButton.setAttribute('type', 'button') + if (!me) newContactButton.setAttribute('disabled', 'true') + authn.checkUser().then(webId => { + if (webId) { + setMe(webId) + newContactButton.removeAttribute('disabled') + } + }) + container.appendChild(newContactButton) + newContactButton.innerHTML = '+ New contact' + newContactButton.classList.add('actionButton', 'btn-primary', 'action-button-focus') + let newContactClickGeneration = 0 + newContactButton.addEventListener('click', async function (_event) { + setActiveActionButton(null) + deselectAllPeople(ctx.ulPeople) + const thisGeneration = ++newContactClickGeneration + ctx.showDetailsSection() + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSectionContent.classList.remove('detailsSectionContent--wide') + await ensureBookLoaded() + // Bail out if a newer click has taken over + if (thisGeneration !== newContactClickGeneration) return + ctx.detailsSectionContent.innerHTML = '' + + const chooserDiv = dom.createElement('div') + chooserDiv.classList.add('contactTypeChooser') + + const selectLabel = dom.createElement('label') + selectLabel.textContent = 'Contact type: ' + selectLabel.setAttribute('for', 'contactTypeSelect') + chooserDiv.appendChild(selectLabel) + + const select = dom.createElement('select') + select.id = 'contactTypeSelect' + select.classList.add('contactTypeSelect') + const optIndividual = dom.createElement('option') + optIndividual.value = 'Individual' + optIndividual.textContent = 'New person' + select.appendChild(optIndividual) + const optOrganization = dom.createElement('option') + optOrganization.value = 'Organization' + optOrganization.textContent = 'New organization' + select.appendChild(optOrganization) + chooserDiv.appendChild(select) + + ctx.detailsSectionContent.appendChild(chooserDiv) + + const remark = dom.createElement('p') + remark.classList.add('contactCreationRemark') + remark.textContent = 'The new contact is added to the already selected group.' + ctx.detailsSectionContent.appendChild(remark) + + // Container for the askName form, placed below the select + const formContainer = dom.createElement('div') + formContainer.classList.add('contactFormContainer') + ctx.detailsSectionContent.appendChild(formContainer) + + function currentKlass () { + return select.value === 'Organization' + ? ns.vcard('Organization') + : ns.vcard('Individual') + } - // For deleting an addressbook sub-folder eg person - use with care! - // @@ move to solid-logic - function deleteRecursive (kb, folder) { - return new Promise(function (resolve) { - kb.fetcher.load(folder).then(function () { - const promises = kb.each(folder, ns.ldp('contains')).map(file => { - if (kb.holds(file, ns.rdf('type'), ns.ldp('BasicContainer'))) { - return deleteRecursive(kb, file) - } else { - console.log('deleteRecirsive file: ' + file) - if (!confirm(' Really DELETE File ' + file)) { - throw new Error('User aborted delete file') - } - return kb.fetcher.webOperation('DELETE', file.uri) - } - }) - console.log('deleteRecirsive folder: ' + folder) - if (!confirm(' Really DELETE folder ' + folder)) { - throw new Error('User aborted delete file') - } - promises.push(kb.fetcher.webOperation('DELETE', folder.uri)) - Promise.all(promises).then(_res => { - resolve() - }) - }) - }) - } + // Render person form immediately as default + createNewPersonOrOrganization(ctx, formContainer, currentKlass()) - function localNode (person, _div) { - const aliases = kb.allAliases(person) - const prefix = book.dir().uri - for (let i = 0; i < aliases.length; i++) { - if (aliases[i].uri.slice(0, prefix.length) === prefix) { - return aliases[i] - } - } - throw new Error('No local URI for ' + person) - } + // Switch form when dropdown changes + select.addEventListener('change', function () { + createNewPersonOrOrganization(ctx, formContainer, currentKlass()) + }) + }, false) - /** Refresh the list of names - */ - function refreshNames () { - function setPersonListener (personRow, person) { - personRow.addEventListener('click', function (event) { - event.preventDefault() - selectPerson(person) - }) - } + // TODO we should also add if it is public or private + header.appendChild(h2) + header.appendChild(container) + headerSection.appendChild(header) + return headerSection +} - let cards = [] - const groups = Object.keys(selectedGroups).map(groupURI => kb.sym(groupURI)) - groups.forEach(group => { - if (selectedGroups[group.value]) { - cards = cards.concat(groupMembers(kb, group)) - } - }) - cards.sort(compareForSort) // @@ sort by name not UID later - for (let k = 0; k < cards.length - 1;) { - if (cards[k].uri === cards[k + 1].uri) { - cards.splice(k, 1) // Eliminate duplicates from more than one group - } else { - k++ - } - } +// ── Builder: search input section ──────────────────────────────────── +function buildSearchSection (ctx) { + const { dom } = ctx + const searchSection = dom.createElement('section') + searchSection.classList.add('searchSection') + const searchDiv = dom.createElement('div') + searchDiv.classList.add('searchDiv') + searchSection.appendChild(searchDiv) + const searchInput = dom.createElement('input') + searchInput.setAttribute('type', 'text') + searchInput.setAttribute('aria-label', 'Search contacts') + searchInput.classList.add('searchInput') + searchInput.setAttribute('placeholder', 'Search by name') + searchDiv.appendChild(searchInput) + searchInput.addEventListener('input', function (_event) { + refreshFilteredPeople(ctx.ulPeople, true, ctx.detailsSectionContent) + }) + return { searchSection, searchInput } +} - peopleHeader.textContent = - cards.length > 5 ? '' + cards.length + ' contacts' : 'contact' - - function renderNameInGroupList (person) { - const personRow = dom.createElement('tr') - const personLeft = personRow.appendChild(dom.createElement('td')) - // const personRight = personRow.appendChild(dom.createElement('td')) - personLeft.setAttribute('style', dataCellStyle) - personRow.subject = person - const name = nameFor(person) - personLeft.textContent = name - personRow.subject = person - UI.widgets.makeDraggable(personRow, person) - - setPersonListener(personRow, person) - return personRow - } +// ── Builder: group buttons bar ─────────────────────────────────────── +function buildGroupBar (ctx) { + const { dom, kb, ns, book, options, groupIndex, selectedGroups, + actionButtons, setActiveActionButton, div } = ctx + + const buttonSection = dom.createElement('section') + buttonSection.classList.add('buttonSection') + + const ulGroups = dom.createElement('ul') + ulGroups.classList.add('groupButtonsList') + ulGroups.setAttribute('role', 'list') + ulGroups.setAttribute('aria-label', 'Groups list') + + if (options.foreignGroup) { + selectedGroups[options.foreignGroup.uri] = true + } + + if (book) { + // All groups button — leftmost, initially selected + ctx.allGroupsLi = dom.createElement('li') + const allGroupsButton = dom.createElement('button') + allGroupsButton.textContent = 'All groups' + allGroupsButton.classList.add('allGroupsButton', 'actionButton', 'btn-primary', 'action-button-focus', 'allGroupsButton--selected') + allGroupsButton.addEventListener('click', function (_event) { + setActiveGroupButton(ulGroups, allGroupsButton) + setActiveActionButton(null) + // Check if all groups are currently selected + const allSelected = Array.from(ulGroups.children).every(function (li) { + if (!li.subject) return true // skip non-group items (All groups, New group) + return !!selectedGroups[li.subject.uri] + }) + + if (!allSelected) { + allGroupsButton.classList.add('allGroupsButton--loading') + allGroupsButton.setAttribute('aria-busy', 'true') + selectAllGroups(selectedGroups, ulGroups, function (ok, message) { + if (!ok) return complain(div, dom, message) + allGroupsButton.classList.remove('allGroupsButton--loading') + allGroupsButton.setAttribute('aria-busy', 'false') + allGroupsButton.classList.add('allGroupsButton--active') + refreshThingsSelected(ulGroups, selectedGroups) + refreshNames(ctx.ulPeople, null) + }) + } else { + allGroupsButton.classList.remove('allGroupsButton--loading', 'allGroupsButton--active') + allGroupsButton.setAttribute('aria-busy', 'false') + allGroupsButton.classList.add('allGroupsButton--loaded') // pale green hint groups loaded + for (const key in selectedGroups) delete selectedGroups[key] + refreshThingsSelected(ulGroups, selectedGroups) + refreshNames(ctx.ulPeople, null) + } + }) // on button click + ctx.allGroupsLi.appendChild(allGroupsButton) + ulGroups.appendChild(ctx.allGroupsLi) // First item in the list + + // New group button — rightmost (appended after group buttons are rendered) + ctx.newGroupLi = dom.createElement('li') + const newGroupButton = dom.createElement('button') + newGroupButton.setAttribute('type', 'button') + newGroupButton.innerHTML = '+ New group' + newGroupButton.classList.add('allGroupsButton', 'actionButton', 'btn-secondary', 'action-button-focus') + actionButtons.push(newGroupButton) + newGroupButton.addEventListener( + 'click', function (event) { + setActiveGroupButton(ulGroups, newGroupButton) + setActiveActionButton(null) + deselectAllPeople(ctx.ulPeople) + handleNewGroupClick(ctx) + }, + false + ) + ctx.newGroupLi.appendChild(newGroupButton) + + // Append ulGroups to buttonSection, then add New group at the end + buttonSection.appendChild(ulGroups) + + if (groupIndex) { + kb.fetcher.nowOrWhenFetched(groupIndex.uri, book, function (ok, body) { + if (!ok) return complain(div, dom, 'Cannot load group index: ' + body) + // Remove special items before sync (syncTableToArrayReOrdered expects .subject on all children) + if (ctx.allGroupsLi.parentNode) ctx.allGroupsLi.parentNode.removeChild(ctx.allGroupsLi) + if (ctx.newGroupLi.parentNode) ctx.newGroupLi.parentNode.removeChild(ctx.newGroupLi) + syncGroupUl(book, options, ulGroups, dom, selectedGroups, ctx.ulPeople, ctx.searchInput) // Refresh list of groups + ulGroups.insertBefore(ctx.allGroupsLi, ulGroups.firstChild) // Keep All contacts first + ulGroups.appendChild(ctx.newGroupLi) // Keep New group last + + // Auto-select all groups and display all contacts on load + allGroupsButton.classList.add('allGroupsButton--loading') + allGroupsButton.setAttribute('aria-busy', 'true') + selectAllGroups(selectedGroups, ulGroups, function (loadOk, message) { + if (!loadOk) return complain(div, dom, message) + allGroupsButton.classList.remove('allGroupsButton--loading') + allGroupsButton.setAttribute('aria-busy', 'false') + allGroupsButton.classList.add('allGroupsButton--active') + refreshThingsSelected(ulGroups, selectedGroups) + refreshNames(ctx.ulPeople, null) + }) + }) + } - utils.syncTableToArrayReOrdered(peopleMainTable, cards, renderNameInGroupList) - refreshFilteredPeople() - } // refreshNames - - function refreshThingsSelected (table, selectionArray) { - for (let i = 0; i < table.children.length; i++) { - const row = table.children[i] - if (row.subject) { - row.setAttribute( - 'style', - selectionArray[row.subject.uri] ? 'background-color: #cce;' : '' - ) - } - } - } + // Remove special items before initial render too + if (ctx.allGroupsLi.parentNode) ctx.allGroupsLi.parentNode.removeChild(ctx.allGroupsLi) + if (ctx.newGroupLi.parentNode) ctx.newGroupLi.parentNode.removeChild(ctx.newGroupLi) + renderGroupButtons(book, ulGroups, options, dom, selectedGroups, ctx.ulPeople, ctx.searchInput, ctx.detailsSectionContent, div, ctx.dataBrowserContext, function () { + setActiveActionButton(null) + // Keep the details section open when a contact or New contact form is showing + if (!ctx.detailsSectionContent.querySelector('.contactTypeChooser, .contactFormContainer, .renderPane')) { + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSection.classList.add('hidden') + } + }) + ulGroups.insertBefore(ctx.allGroupsLi, ulGroups.firstChild) // Keep All contacts first + ulGroups.appendChild(ctx.newGroupLi) // Ensure New group is last after initial render + } else { + syncGroupUl(book, options, ulGroups, dom, selectedGroups, ctx.ulPeople, ctx.searchInput) // Refresh list of groups (will be empty) + refreshNames(ctx.ulPeople, null) + debug.log('No book, only one group -> hide list of groups') + } // if not book + + return { buttonSection, ulGroups } +} - function refreshGroupsSelected () { - return refreshThingsSelected(groupsMainTable, selectedGroups) - } +// ── Builder: footer action buttons (Groups / Sharing / Tools) ──────── +function buildFooterButtons (ctx) { + const { dom, kb, ns, book, options, selectedGroups, + actionButtons, setActiveActionButton, dataBrowserContext, div, me } = ctx + + const cardFooter = dom.createElement('div') + cardFooter.classList.add('cardFooter') + + if (book) { + // Groups button + const groupsButton = cardFooter.appendChild(dom.createElement('button')) + groupsButton.setAttribute('type', 'button') + groupsButton.innerHTML = 'Groups' + groupsButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + actionButtons.push(groupsButton) + groupsButton.addEventListener('click', async function (_event) { + setActiveActionButton(groupsButton) + deselectAllPeople(ctx.ulPeople) + ctx.showDetailsSection() + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSectionContent.classList.remove('detailsSectionContent--wide') + + // Header + const groupsHeader = dom.createElement('h3') + groupsHeader.textContent = 'Your groups' + ctx.detailsSectionContent.appendChild(groupsHeader) + + const groupRemark = dom.createElement('p') + groupRemark.textContent = 'When you delete a group it can happen that some contacts end up groupless.' + ctx.detailsSectionContent.appendChild(groupRemark) + + // Load all groups and display them in a list + let groups + try { + groups = await loadAllGroups(book) + } catch (err) { + ctx.detailsSectionContent.appendChild(dom.createTextNode('Failed to load groups: ' + err)) + return + } - // Check every group is in the list and add it if not. - - function syncGroupTable () { - function renderGroupRow (group) { - // Is something is dropped on a group, add people to group - async function handleURIsDroppedOnGroup (uris) { - uris.forEach(function (u) { - console.log('Dropped on group: ' + u) - const thing = kb.sym(u) - try { - addPersonToGroup(thing, group) - } catch (e) { - complain(e) - } - refreshNames() - }) + const groupsList = dom.createElement('ul') + groupsList.setAttribute('role', 'list') + groupsList.setAttribute('aria-label', 'All groups') + groupsList.classList.add('groupButtonsList') + + // Sort groups by name + if (groups) { + groups.sort((a, b) => { + const nameA = (kb.any(a, ns.vcard('fn')) || '').toString().toLowerCase() + const nameB = (kb.any(b, ns.vcard('fn')) || '').toString().toLowerCase() + return nameA < nameB ? -1 : nameA > nameB ? 1 : 0 + }) + groups.forEach(function (group) { + const { groupLi, groupButton: groupBtn, name } = createGroupLi(group) + groupBtn.addEventListener('click', function (event) { + event.preventDefault() + if (!event.metaKey) { + for (const key in selectedGroups) delete selectedGroups[key] } - function groupRowClickListener (event) { - event.preventDefault() - const groupList = kb.sym(group.uri.split('#')[0]) - if (!event.metaKey) { - selectedGroups = {} // If Command key pressed, accumulate multiple + selectedGroups[group.uri] = !selectedGroups[group.uri] + refreshThingsSelected(ctx.ulGroups, selectedGroups) + // Highlight the matching group button in the sidebar ulGroups + const matchingLi = Array.from(ctx.ulGroups.children).find(li => li.subject && li.subject.uri === group.uri) + setActiveGroupButton(ctx.ulGroups, matchingLi ? matchingLi.querySelector('button') : null) + kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (ok, _message) { + if (ok) { + refreshNames(ctx.ulPeople, null, false) } - selectedGroups[group.uri] = !selectedGroups[group.uri] - refreshGroupsSelected() - peopleMainTable.innerHTML = '' // clear in case refreshNames doesn't work for unknown reason - - kb.fetcher.nowOrWhenFetched( - groupList.uri, - undefined, - function (ok, message) { - if (!ok) { - return complainIfBad( - ok, - 'Can\'t load group file: ' + groupList + ': ' + message - ) - } - refreshNames() - - if (!event.metaKey) { - // If only one group has been selected, show ACL - cardMain.innerHTML = '' - let visible = false - const aclControl = UI.aclControl.ACLControlBox5( - group, - dataBrowserContext, - 'group', - kb, - function (ok, body, response) { - if (!ok) { - if (response && response.status && response.status === 403) { - cardMain.innerHTML = 'No control access.' - } else { - cardMain.innerHTML = 'Failed to load access control: ' + body - } - } - } - ) - const sharingButton = cardMain.appendChild( - dom.createElement('button') - ) - sharingButton.style.cssText = - 'padding: 1em; margin: 1em' - const img = sharingButton.appendChild( - dom.createElement('img') - ) - img.style.cssText = 'width: 1.5em; height: 1.5em' - img.setAttribute( - 'src', - UI.icons.iconBase + 'noun_123691.svg' - ) - sharingButton.addEventListener('click', function () { - visible = !visible - if (visible) { - cardMain.appendChild(aclControl) - } else { - cardMain.removeChild(aclControl) - } - }) - } - } - ) + }) + }, false) + + UI.widgets.deleteButtonWithCheck( + dom, + groupLi, + 'group ' + name, + async function () { + await deleteThingAndDoc(group) + delete selectedGroups[group.uri] + // Refresh the group buttons list + const allGroupsLi = ctx.allGroupsLi + const newGroupLi = ctx.newGroupLi + if (allGroupsLi.parentNode) allGroupsLi.parentNode.removeChild(allGroupsLi) + if (newGroupLi.parentNode) newGroupLi.parentNode.removeChild(newGroupLi) + syncGroupUl(book, options, ctx.ulGroups, dom, selectedGroups, ctx.ulPeople, ctx.searchInput) + ctx.ulGroups.insertBefore(allGroupsLi, ctx.ulGroups.firstChild) + ctx.ulGroups.appendChild(newGroupLi) + refreshThingsSelected(ctx.ulGroups, selectedGroups) + // Refresh the people list to reflect the deleted group + refreshNames(ctx.ulPeople, null, false) + // Refresh the groups detail view + groupsButton.click() } - - // Body of renderGroupRow - const name = kb.any(group, ns.vcard('fn')) - const groupRow = dom.createElement('tr') - groupRow.subject = group - UI.widgets.makeDraggable(groupRow, group) - - groupRow.setAttribute('style', dataCellStyle) - groupRow.textContent = name - - UI.widgets.makeDropTarget(groupRow, handleURIsDroppedOnGroup) - UI.widgets.deleteButtonWithCheck( - dom, - groupRow, - 'group ' + name, - async function () { - await deleteThingAndDoc(group) - syncGroupTable() - } - ) - groupRow.addEventListener('click', groupRowClickListener, true) - return groupRow - } // renderGroupRow - - const groups = groupsInOrder() - utils.syncTableToArrayReOrdered(groupsMainTable, groups, renderGroupRow) - refreshGroupsSelected() - // await checkDataModel(groups) - } // syncGroupTable - - async function checkDataModel () { - // await kb.fetcher.load(groups) // asssume loaded already - const groups = await loadAllGroups() - - const { del, ins } = await getDataModelIssues(groups) - - if (del.length && confirm(`Groups data model need to be updated? (${del.length})`)) { - await kb.updater.updateMany(del, ins) - alert('Update done') - } - } // checkDataModel - - // Click on New Group button - async function newGroupClickHandler (_event) { - cardMain.innerHTML = '' - const groupIndex = kb.any(book, ns.vcard('groupIndex')) - try { - await kb.fetcher.load(groupIndex) - } catch (e) { - console.log('Error: Group index NOT loaded:' + e + '\n') - } - console.log(' Group index has been loaded\n') - - const name = await UI.widgets.askName( - dom, kb, cardMain, UI.ns.foaf('name'), ns.vcard('Group'), 'group') - if (!name) return // cancelled by user - let group - try { - group = await saveNewGroup(book, name) - } catch (err) { - console.log('Error: can\'t save new group:' + err) - cardMain.innerHTML = 'Failed to save group' + err - return - } - selectedGroups = {} - selectedGroups[group.uri] = true - syncGroupTable() // Refresh list of groups - - cardMain.innerHTML = '' - cardMain.appendChild(UI.aclControl.ACLControlBox5( - group.doc(), dataBrowserContext, 'group', kb, - function (ok, body) { - if (!ok) { - cardMain.innerHTML = - 'Group sharing setup failed: ' + body - } - })) - } // newGroupClickHandler - - async function craeteNewCard (klass) { - cardMain.innerHTML = '' - const ourBook = findBookFromGroups(book) - try { - await kb.fetcher.load(ourBook) - } catch (err) { - throw new Error('Book won\'t load:' + ourBook) - } - - const nameEmailIndex = kb.any(ourBook, ns.vcard('nameEmailIndex')) - if (!nameEmailIndex) throw new Error('Wot no nameEmailIndex?') - await kb.fetcher.load(nameEmailIndex) - // console.log('Name index loaded async' + nameEmailIndex) - - const name = await UI.widgets - .askName(dom, kb, cardMain, UI.ns.foaf('name'), klass) // @@ was, 'person' - - if (!name) return // cancelled by user - cardMain.innerHTML = 'indexing...' - book = findBookFromGroups(book) - let person - try { - person = await saveNewContact(book, name, selectedGroups, klass) - } catch (err) { - const msg = 'Error: can\'t save new contact: ' + err - console.log(msg) - alert(msg) - } - - selectedPeople = {} - selectedPeople[person.uri] = true - refreshNames() // Add name to list of group - cardMain.innerHTML = '' // Clear 'indexing' - cardMain.appendChild(renderPane(dom, person, 'contact')) - } - - // //////////////////////////// Three-column Contact Browser - Body - // ////////////////// Body of 3-column browser - - const bookTable = dom.createElement('table') - bookTable.setAttribute( - 'style', - 'border-collapse: collapse; margin-right: 0; max-height: 9in;' - ) - div.appendChild(bookTable) - /* - bookTable.innerHTML = ` - - - - - - - - - - - - ` - */ - const bookHeader = bookTable.appendChild(dom.createElement('tr')) - const bookMain = bookTable.appendChild(dom.createElement('tr')) - const bookFooter = bookTable.appendChild(dom.createElement('tr')) - - const groupsHeader = bookHeader.appendChild(dom.createElement('td')) - const peopleHeader = bookHeader.appendChild(dom.createElement('td')) - const cardHeader = bookHeader.appendChild(dom.createElement('td')) - - const groupsMain = bookMain.appendChild(dom.createElement('td')) - const groupsMainTable = groupsMain.appendChild(dom.createElement('table')) - const peopleMain = bookMain.appendChild(dom.createElement('td')) - const peopleMainTable = peopleMain.appendChild(dom.createElement('table')) - - const groupsFooter = bookFooter.appendChild(dom.createElement('td')) - const peopleFooter = bookFooter.appendChild(dom.createElement('td')) - const cardFooter = bookFooter.appendChild(dom.createElement('td')) - - cardHeader.appendChild(dom.createElement('div')) // searchDiv - // searchDiv.setAttribute('style', 'border: 0.1em solid #888; border-radius: 0.5em') - const searchInput = cardHeader.appendChild(dom.createElement('input')) - searchInput.setAttribute('type', 'text') - searchInput.style = style.searchInputStyle || - 'border: 0.1em solid #444; border-radius: 0.5em; width: 100%; font-size: 100%; padding: 0.1em 0.6em' - - searchInput.addEventListener('input', function (_event) { - refreshFilteredPeople(true) // Active: select person if just one left - }) - - const cardMain = bookMain.appendChild(dom.createElement('td')) - cardMain.setAttribute('style', 'margin: 0;') // fill space available - const dataCellStyle = 'padding: 0.1em;' - - groupsHeader.textContent = 'groups' - groupsHeader.setAttribute( - 'style', - 'min-width: 10em; padding-bottom 0.2em;' - ) - - function setGroupListVisibility (visible) { - const vis = visible ? '' : 'display: none;' - groupsHeader.setAttribute( - 'style', - 'min-width: 10em; padding-bottom 0.2em;' + vis ) - const hfstyle = 'padding: 0.1em;' - groupsMain.setAttribute('style', hfstyle + vis) - groupsFooter.setAttribute('style', hfstyle + vis) - } - setGroupListVisibility(true) - if (options.foreignGroup) { - selectedGroups[options.foreignGroup.uri] = true - } - if (book) { - const allGroups = groupsHeader.appendChild(dom.createElement('button')) - allGroups.textContent = 'All' - const style = 'margin-left: 1em; font-size: 100%;' - allGroups.setAttribute('style', style) - allGroups.addEventListener('click', function (_event) { - allGroups.state = allGroups.state ? 0 : 1 - peopleMainTable.innerHTML = '' // clear in case refreshNames doesn't work for unknown reason - if (allGroups.state) { - allGroups.setAttribute('style', style + 'background-color: #ff8;') - selectAllGroups(selectedGroups, groupsMainTable, function ( - ok, - message - ) { - if (!ok) return complain(message) - allGroups.setAttribute( - 'style', - style + 'background-color: black; color: white' - ) - refreshGroupsSelected() - }) - } else { - allGroups.setAttribute('style', style + 'background-color: #cfc;') // pale green hint groups loaded - selectedGroups = {} - refreshGroupsSelected() - } - }) // on button click - kb.fetcher.nowOrWhenFetched(groupIndex.uri, book, function (ok, body) { - if (!ok) return console.log('Cannot load group index: ' + body) - syncGroupTable() - refreshNames() - }) - } else { - syncGroupTable() - refreshNames() - console.log('No book, only one group -> hide list of groups') - setGroupListVisibility(false) // If no books involved, hide group list - } // if not book - - peopleHeader.textContent = 'name' - peopleHeader.setAttribute('style', 'min-width: 18em;') - peopleMain.setAttribute('style', 'overflow:scroll;') - - // New Contact button - const newContactButton = dom.createElement('button') - const container = dom.createElement('div') - newContactButton.setAttribute('type', 'button') - if (!me) newContactButton.setAttribute('disabled', 'true') - authn.checkUser().then(webId => { - if (webId) { - me = webId - newContactButton.removeAttribute('disabled') - } - }) - container.appendChild(newContactButton) - newContactButton.innerHTML = 'New Contact' // + IndividualClassLabel - peopleFooter.appendChild(container) - newContactButton.addEventListener('click', async _event => craeteNewCard(ns.vcard('Individual')), false) - - // New Organization button - const newOrganizationButton = dom.createElement('button') - const container2 = dom.createElement('div') - newOrganizationButton.setAttribute('type', 'button') - if (!me) newOrganizationButton.setAttribute('disabled', 'true') - authn.checkUser().then(webId => { - if (webId) { - me = webId - newOrganizationButton.removeAttribute('disabled') - } + groupsList.appendChild(groupLi) }) - container2.appendChild(newOrganizationButton) - newOrganizationButton.innerHTML = 'New Organization' // + IndividualClassLabel - peopleFooter.appendChild(container2) - newOrganizationButton.addEventListener('click', async _event => craeteNewCard(ns.vcard('Organization')), false) - - // New Group button - if (book) { - const newGroupButton = groupsFooter.appendChild( - dom.createElement('button') - ) - newGroupButton.setAttribute('type', 'button') - newGroupButton.innerHTML = 'New Group' // + IndividualClassLabel - newGroupButton.addEventListener( - 'click', newGroupClickHandler, - false - ) - - // Tools button - const toolsButton = cardFooter.appendChild(dom.createElement('button')) - toolsButton.setAttribute('type', 'button') - toolsButton.innerHTML = 'Tools' - toolsButton.addEventListener('click', function (_event) { - cardMain.innerHTML = '' - cardMain.appendChild( - toolsPane( - selectAllGroups, - selectedGroups, - groupsMainTable, - book, - dataBrowserContext, - me - ) - ) - }) - } // if book - - cardFooter.appendChild(newAddressBookButton(book)) - - // }) - - div.appendChild(dom.createElement('hr')) - - // const groups = await loadAllGroups() @@@ - checkDataModel().then(() => { console.log('async checkDataModel done.') }) - - // div.appendChild(newAddressBookButton(book)) // later - // end of AddressBook instance - } // renderThreeColumnBrowser - - // /////////////////////////////////////////////////////////////////////////////////// - - // Render a single contact Individual - - if ( - t[ns.vcard('Individual').uri] || - t[ns.foaf('Person').uri] || - t[ns.schema('Person').uri] || - t[ns.vcard('Organization').uri] || - t[ns.schema('Organization').uri] - ) { - renderIndividual(dom, div, subject, dataBrowserContext).then(() => console.log('(individual rendered)')) - - // Render a Group instance - } else if (t[ns.vcard('Group').uri]) { - // If we have a main address book, then render this group as a guest group within it - UI.login - .findAppInstances(context, ns.vcard('AddressBook')) - .then(function (context) { - const addressBooks = context.instances - const options = { foreignGroup: subject } - if (addressBooks.length > 0) { - // const book = addressBooks[0] - renderThreeColumnBrowser(addressBooks, context, options) - } else { - renderThreeColumnBrowser([], context, options) - // @@ button to Make a new addressBook - } - }) - .catch(function (e) { - UI.widgets.complain(context, e) - }) - - // Render a AddressBook instance - } else if (t[ns.vcard('AddressBook').uri]) { - renderThreeColumnBrowser([subject], context, {}) - } else { - console.log( - 'Error: Contact pane: No evidence that ' + - subject + - ' is anything to do with contacts.' - ) } - me = authn.currentUser() - if (!me) { - console.log( - '(You do not have your Web Id set. Sign in or sign up to make changes.)' - ) - UI.login.ensureLoadedProfile(context).then( - context => { - console.log('Logged in as ' + context.me) - me = context.me - }, - err => { - div.appendChild(UI.widgets.errorMessageBlock(err)) + ctx.detailsSectionContent.appendChild(groupsList) + + // New group button at the bottom + const newGroupBtn = dom.createElement('button') + newGroupBtn.setAttribute('type', 'button') + newGroupBtn.innerHTML = '+ New group' + newGroupBtn.classList.add('actionButton', 'btn-primary', 'action-button-focus', 'newGroupBtn') + newGroupBtn.addEventListener('click', function () { handleNewGroupClick(ctx) }, false) + ctx.detailsSectionContent.appendChild(newGroupBtn) + }) + + // Sharing button + const sharingButton = cardFooter.appendChild(dom.createElement('button')) + sharingButton.setAttribute('type', 'button') + sharingButton.innerHTML = 'Sharing' + sharingButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + actionButtons.push(sharingButton) + sharingButton.addEventListener('click', function (_event) { + setActiveActionButton(sharingButton) + deselectAllPeople(ctx.ulPeople) + ctx.showDetailsSection() + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSectionContent.classList.remove('detailsSectionContent--wide') + + const sharingHeader = dom.createElement('h3') + sharingHeader.textContent = 'Sharing' + ctx.detailsSectionContent.appendChild(sharingHeader) + + ctx.detailsSectionContent.appendChild( + UI.aclControl.ACLControlBox5( + book.dir(), + dataBrowserContext, + 'book', + kb, + function (ok, body) { + if (!ok) ctx.detailsSectionContent.innerHTML = 'ACL control box Failed: ' + body } ) - } else { - // console.log("(Your webid is "+ me +")") - } - - // /////////////// Fix user when testing on a plane + ) - if ( - typeof document !== 'undefined' && - document.location && - ('' + document.location).slice(0, 16) === 'http://localhost' - ) { - me = kb.any(subject, UI.ns.acl('owner')) // when testing on plane with no webid - console.log('Assuming user is ' + me) + const sharingContext = { + target: book, + me, + noun: 'address book', + div: ctx.detailsSectionContent, + dom, + statusRegion: div } - return div - } // asyncRender - } // render function -} // pane object -// ends + UI.login.registrationControl(sharingContext, book, ns.vcard('AddressBook')) + .then(() => debug.log('Registration control finished.')) + .catch(e => UI.widgets.complain(sharingContext, 'registrationControl: ' + e)) + }) + + // Settings button + const toolsButton = cardFooter.appendChild(dom.createElement('button')) + toolsButton.setAttribute('type', 'button') + toolsButton.innerHTML = 'Tools' + toolsButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + actionButtons.push(toolsButton) + toolsButton.addEventListener('click', function (_event) { + setActiveActionButton(toolsButton) + deselectAllPeople(ctx.ulPeople) + ctx.showDetailsSection() + ctx.detailsSectionContent.innerHTML = '' + ctx.detailsSectionContent.classList.add('detailsSectionContent--wide') + ctx.detailsSectionContent.appendChild( + toolsPane( + selectAllGroups, + selectedGroups, + ctx.ulGroups, + book, + dataBrowserContext, + me, + function refreshGroups () { + if (ctx.allGroupsLi.parentNode) ctx.allGroupsLi.parentNode.removeChild(ctx.allGroupsLi) + if (ctx.newGroupLi.parentNode) ctx.newGroupLi.parentNode.removeChild(ctx.newGroupLi) + syncGroupUl(book, options, ctx.ulGroups, dom, selectedGroups, ctx.ulPeople, ctx.searchInput) + ctx.ulGroups.insertBefore(ctx.allGroupsLi, ctx.ulGroups.firstChild) + ctx.ulGroups.appendChild(ctx.newGroupLi) + refreshThingsSelected(ctx.ulGroups, selectedGroups) + } + ) + ) + }) + } // if book -export function getSameAs (kb, item, doc) { - return kb.each(item, ns.owl('sameAs'), null, doc).concat( - kb.each(null, ns.owl('sameAs'), item, doc)) + return cardFooter } diff --git a/src/debug.js b/src/debug.js new file mode 100644 index 0000000..00648ef --- /dev/null +++ b/src/debug.js @@ -0,0 +1,15 @@ +export function log (...args) { + console.log(...args) +} + +export function warn (...args) { + console.warn(...args) +} + +export function error (...args) { + console.error(...args) +} + +export function trace (...args) { + console.trace(...args) +} diff --git a/src/groupMembershipControl.js b/src/groupMembershipControl.js index 6ea6140..b3c056b 100644 --- a/src/groupMembershipControl.js +++ b/src/groupMembershipControl.js @@ -1,19 +1,18 @@ // Render a control to record the group memberships we have for this agent import * as UI from 'solid-ui' import { store } from 'solid-logic' +import './styles/groupMembership.css' +import * as debug from './debug' +import { normalizeGroupUri } from './localUtils' const ns = UI.ns -// const buttons = UI.buttonsn no -// const widgets = UI.widgets -const utils = UI.utils -// const style = UI.style const kb = store // Groups the person is a member of export function groupMembership (person) { let groups = kb.statementsMatching(null, ns.owl('sameAs'), person).map(st => st.why) .concat(kb.each(null, ns.vcard('hasMember'), person)) - const strings = new Set(groups.map(group => group.uri)) // remove dups + const strings = new Set(groups.map(group => normalizeGroupUri(group.uri))) // remove dups with normalized URIs groups = [...strings].map(uri => kb.sym(uri)) return groups } @@ -49,43 +48,78 @@ export async function renderGroupMemberships (person, context) { del = del.concat(kb.statementsMatching(undefined, undefined, webid, group.doc())) } }) - kb.updater.update(del, [], function (uri, ok, err) { - if (!ok) { - const message = 'Error removing member from group ' + group + ': ' + err - groupList.parentNode.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) - } - }) - console.log('Removed ' + pname + ' from group ' + gname) + try { + await kb.updater.update(del, []) + } catch (err) { + const message = 'Error removing member from group ' + group + ': ' + err + container.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) + return + } + debug.log('Removed ' + pname + ' from group ' + gname) // to allow refresh of card groupList kb.fetcher.unload(group.doc()) await kb.fetcher.load(group.doc()) - syncGroupList() + syncGroupPills() } } - function newRowForGroup (group) { - const options = { - deleteFunction: function () { + function createGroupItem (group) { + const gname = kb.any(group, ns.vcard('fn')) + const label = gname ? gname.value : group.uri + + const li = dom.createElement('li') + li.classList.add('group-membership-item') + + // Main group button + const btn = dom.createElement('button') + btn.setAttribute('type', 'button') + btn.classList.add('allGroupsButton', 'actionButton', 'btn-secondary', 'action-button-focus') + btn.textContent = label + btn.title = label + li.appendChild(btn) + + // Toolbar below the button: link icon + delete button + const toolbar = dom.createElement('div') + toolbar.classList.add('group-membership-toolbar') + + // Link icon + const linkEl = UI.widgets.linkIcon(dom, group) + linkEl.setAttribute('title', 'Link to ' + label) + toolbar.appendChild(linkEl) + + // Delete button + UI.widgets.deleteButtonWithCheck( + dom, + toolbar, + 'membership in ' + label, + function () { removeFromGroup(person, group) - }, - noun: 'membership' - } - const tr = UI.widgets.personTR(dom, null, group, options) - return tr + } + ) + + li.appendChild(toolbar) + return li } - // find all groups where person has membership - function syncGroupList () { - // person and/or WebIDs to be changed - utils.syncTableToArray(groupList, groupMembership(person), newRowForGroup) + function syncGroupPills () { + const groups = groupMembership(person) + const pillsWrapper = container.querySelector('.group-pills-wrapper') + if (groups.length === 0) { + pillsWrapper.innerHTML = 'Not part of any Address Book groups.' + } else { + pillsWrapper.innerHTML = '' + } + + groups.forEach(group => { + pillsWrapper.appendChild(createGroupItem(group)) + }) } async function loadGroupsFromBook (book = null) { if (!book) { book = kb.any(undefined, ns.vcard('includesGroup')) if (!book) { - // throw new Error('findBookFromGroups: Cant find address book which this group is part of') - return // no book => no groups + return // no book => no groups } } const groupIndex = kb.any(book, ns.vcard('groupIndex')) @@ -95,12 +129,24 @@ export async function renderGroupMemberships (person, context) { const { dom } = context const kb = context.session.store - const groupList = dom.createElement('table') + + const container = dom.createElement('div') + container.classList.add('group-membership-container') + + // Header + const header = dom.createElement('h3') + header.classList.add('group-membership-header') + header.textContent = 'Part of groups' + container.appendChild(header) + + const pillsWrapper = dom.createElement('ul') + pillsWrapper.classList.add('group-pills-wrapper') + container.appendChild(pillsWrapper) // find book any group and load all groups await loadGroupsFromBook() - groupList.refresh = syncGroupList - syncGroupList() - return groupList + container.refresh = syncGroupPills + syncGroupPills() + return container } diff --git a/src/individual.js b/src/individual.js index e0f5730..40bc411 100644 --- a/src/individual.js +++ b/src/individual.js @@ -3,127 +3,71 @@ import { authn, store } from 'solid-logic' import { renderMugshotGallery } from './mugshotGallery' import { renderWebIdControl, renderPublicIdControl } from './webidControl' import { renderGroupMemberships } from './groupMembershipControl' -import textOfForms from './ontology/forms.ttl' +import formsSource from './ontology/individualAndOrganizationForm.ttl' import VCARD_ONTOLOGY_TEXT from './ontology/vcard.ttl' -import * as $rdf from 'rdflib' +import './styles/individual.css' +import './styles/rdfFormsEnforced.css' +import { renderForm, loadDocument } from './rdfFormsHelper' +import { complain } from './localUtils' const ns = UI.ns const kb = store -const style = UI.style - -export function loadTurtleText (kb, thing, text) { - const doc = thing.doc() - if (!kb.holds(undefined, undefined, undefined, doc)) { - // If not loaded already - $rdf.parse(text, kb, doc.uri, 'text/turtle') // Load directly - } -} +const formsName = 'individualAndOrganizationForm.ttl' // The name of the form file +const vcardName = 'vcard.ttl' // The name of the vcard file // Render Individual card export async function renderIndividual (dom, div, subject, dataBrowserContext) { // //////////////////// DRAG and Drop for mugshot image - function complain (message) { - console.log(message) - div.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) - } - - function spacer () { - div - .appendChild(dom.createElement('div')) - .setAttribute('style', 'height: 1em') - } - function complainIfBad (ok, body) { - if (!ok) { - complain('Error: ' + body) - } - } - - /// /////////////////////////// const t = kb.findTypeURIs(subject) const isOrganization = !!(t[ns.vcard('Organization').uri] || t[ns.schema('Organization').uri]) const editable = kb.updater.editable(subject.doc().uri, kb) - const individualForm = kb.sym( - 'https://solid.github.io/solid-panes/contact/individualForm.ttl#form1' - ) - loadTurtleText(kb, individualForm, textOfForms) + // We load the local form document + loadDocument(kb, formsSource, formsName) - const orgDetailsForm = kb.sym( // orgDetailsForm organizationForm - 'https://solid.github.io/solid-panes/contact/individualForm.ttl#orgDetailsForm' - ) - - // Ontology metadata for this pane we bundle with the JS - const vcardOnt = UI.ns.vcard('Type').doc() - if (!kb.holds(undefined, undefined, undefined, vcardOnt)) { - // If not loaded already - $rdf.parse(VCARD_ONTOLOGY_TEXT, kb, vcardOnt.uri, 'text/turtle') // Load ontology directly - } + // We need to make sure VCARD ontology is loaded in the store + const vcardOntUri = UI.ns.vcard('Type').doc().uri // URI to VCARD + loadDocument(kb, VCARD_ONTOLOGY_TEXT, vcardName, vcardOntUri) try { await kb.fetcher.load(subject.doc()) } catch (err) { - complain('Error: Failed to load contact card: ' + err) + complain('Error: Failed to load profile card: ' + err) } // end of try catch on load - div.style = style.paneDivStyle || 'padding: 0.5em 1.5em 1em 1.5em;' + div.classList.add('individualPane') authn.checkUser() // kick off async operation @@@ use async version div.appendChild(renderMugshotGallery(dom, subject)) - const form = isOrganization ? orgDetailsForm : individualForm - UI.widgets.appendForm( - dom, - div, - {}, - subject, - form, - subject.doc(), - complainIfBad - ) + const whichForm = isOrganization ? 'organizationForm' : 'individualForm' - spacer() + renderForm(div, subject, formsSource, formsName, store, dom, subject.doc(), whichForm) div.appendChild(await renderGroupMemberships(subject, dataBrowserContext)) - spacer() - - // Auto complete searches in a table - // Prefer the fom below renderPublicIdControl - /* - if (isOrganization) { - const publicDataTable = div.appendChild(dom.createElement('table')) - async function publicDataSearchRow (name) { - async function autoCompleteDone (object, _name) { - right.innerHTML = '' - right.appendchild(UI.widgets.personTR(dom, object)) - } - const row = dom.createElement('tr') - const left = row.appendChild(dom.createElement('td')) - left.textContent = name - const right = row.appendChild(dom.createElement('td')) - right.appendChild(await renderAutoComplete(dom, subject, ns.owl('sameAs'), autoCompleteDone)) - return row - } - publicDataTable.appendChild(await publicDataSearchRow('dbpedia')) + if ( authn.currentUser()) { + // Allow to attach documents etc to the contact card + const h3 = div.appendChild(dom.createElement('h3')) + h3.textContent = 'Attach any document to this contact' + h3.classList.add('webidHeading') + + UI.widgets.attachmentList(dom, subject, div, { + modify: editable + // promptIcon: UI.icons.iconBase + 'noun_681601.svg', + // predicate: UI.ns.vcard('url') // @@@@@@@@@ ,--- no, the vcard ontology structure uses a bnode. + }) } -*/ - // Allow to attach documents etc to the contact card - - UI.widgets.attachmentList(dom, subject, div, { - modify: editable - // promptIcon: UI.icons.iconBase + 'noun_681601.svg', - // predicate: UI.ns.vcard('url') // @@@@@@@@@ ,--- no, the vcard ontology structure uses a bnode. - }) - - spacer() if (isOrganization) { div.appendChild(await renderPublicIdControl(subject, dataBrowserContext)) } else { - div.appendChild(await renderWebIdControl(subject, dataBrowserContext)) + // if we are already displaying a WebID, we do not need to add one + if (!kb.holds(subject, ns.rdf('type'), ns.foaf('PersonalProfileDocument'))) { + div.appendChild(await renderWebIdControl(subject, dataBrowserContext)) + } } - // div.appendChild(dom.createElement('hr')) } // renderIndividual diff --git a/src/instituteDetailsQuery.sparql b/src/instituteDetailsQuery.sparql deleted file mode 100644 index f9c57cb..0000000 --- a/src/instituteDetailsQuery.sparql +++ /dev/null @@ -1,36 +0,0 @@ -prefix vcard: -CONSTRUCT -{ wd:Q49108 vcard:fn ?itemLabel. -wd:Q49108 rdf:type ?klass. ?klass rdfs:label ?klassLabel; rdfs:comment ?klassDescription . -wd:Q49108 schema:logo ?logo; - schema:image ?image; - schema:logo ?sealImage; - schema:subOrganization ?subsidiary . - ?subsidiary rdfs:label ?subsidiaryLabel . - ?supersidiary schema:subOrganization wd:Q49108 . - ?supersidiary rdfs:label ?supersidiaryLabel . - wd:Q49108 schema:location ?location . - ?location schema:elevation ?elevation . - ?location wdt:P131 ?region . ?region rdfs:label ?regionLabel . - ?location wdt:P625 ?coordinates . - ?location schema:country ?country . ?country rdfs:label ?countryLabel . -} -WHERE -{ optional {wd:Q49108 rdfs:label ?itemLabel} . - optional {wd:Q49108 wdt:P154 ?logo .} - optional {wd:Q49108 wdt:P31 ?klass .} - optional {wd:Q49108 wdt:P158 ?sealImage .} - optional {wd:Q49108 wdt:P18 ?image .} - - optional { wd:Q49108 wdt:P355 ?subsidiary . } - optional { ?supersidiary wdt:P355 wd:Q49108. } - - optional { wd:Q49108 wdt:P276 ?location . - - optional { ?location schema:eleveation ?elevation } - optional { ?location wdt:P131 ?region } - optional { ?location wdt:P625 ?coordinates } - optional { ?location wdt:P17 ?country } - } - SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en,de,it". } -} diff --git a/src/localUtils.js b/src/localUtils.js new file mode 100644 index 0000000..8cf882e --- /dev/null +++ b/src/localUtils.js @@ -0,0 +1,105 @@ +import * as debug from './debug' +import * as UI from 'solid-ui' +import { store } from 'solid-logic' + +const kb = store +const ns = UI.ns +let dom + +export function setDom (d) { + dom = d +} + +/** + * Normalize group URIs to ensure consistent representation. + * Groups should be referenced with fragment #this, e.g., ...Group/AnotherGroup.ttl#this + * If a group URI ends with .ttl (without #this), add #this + * @param {string} uri - The group URI to normalize + * @returns {string} The normalized group URI + */ +export function normalizeGroupUri (uri) { + if (uri && uri.endsWith('.ttl')) { + return uri + '#this' + } + return uri +} + +export function complain (div, d, message) { + debug.error('contactsPane: ' + message) + UI.widgets.errorMessageBlock(d, message, 'pink') +} +export function complainIfBad (div, dom, ok, body) { + if (!ok) { + complain(div, dom, 'Error: ' + body) + } +} + +export function getSameAs (kb, item, doc) { + return kb.each(item, ns.owl('sameAs'), null, doc).concat( + kb.each(null, ns.owl('sameAs'), item, doc)) +} +// For deleting an addressbook sub-folder eg person - use with care! +// @@ move to solid-logic +export function deleteRecursive (kb, folder) { + return new Promise(function (resolve, reject) { + kb.fetcher.load(folder).then(function () { + const promises = kb.each(folder, ns.ldp('contains')).map(file => { + if (kb.holds(file, ns.rdf('type'), ns.ldp('BasicContainer'))) { + return deleteRecursive(kb, file) + } else { + debug.log('Recursie delete - we delete file ' + file.uri) + return kb.fetcher.webOperation('DELETE', file.uri) + } + }) + debug.log('Recursie delete - we delete folder ' + folder.uri) + promises.push(kb.fetcher.webOperation('DELETE', folder.uri)) + Promise.all(promises).then(_res => { + resolve() + }).catch(reject) + }).catch(reject) + }) +} + +// In a LDP work, deletes the whole document describing a thing +// plus patch out ALL mentiosn of it! Use with care! +// beware of other data picked up from other places being smushed +// together and then deleted. +export async function deleteThingAndDoc (x) { + const name = nameFor(x) + if (!confirm('Really DELETE ' + name + '?')) { + return + } + debug.log('deleteThingAndDoc - to be deleted ' + x) + const ds = kb.statementsMatching(x).concat(kb.statementsMatching(undefined, undefined, x)) + try { + await kb.updater.updateMany(ds) + await kb.fetcher.delete(x.doc()) + debug.log('deleteThingAndDoc - deleted') + } catch (err) { + UI.widgets.complain('Error deleting ' + x + ': ' + err) + throw err + } +} + +export function compareForSort (self, other) { + let s = nameFor(self) + let o = nameFor(other) + if (s && o) { + s = s.toLowerCase() + o = o.toLowerCase() + if (s > o) return 1 + if (s < o) return -1 + } + if (self.uri > other.uri) return 1 + if (self.uri < other.uri) return -1 + return 0 +} + +// organization-name is a hack for Mac records with no FN which is mandatory. +export function nameFor (x) { + const name = + kb.any(x, ns.vcard('fn')) || + kb.any(x, ns.foaf('name')) || + kb.any(x, ns.vcard('organization-name')) + return name ? name.value : '???' +} diff --git a/src/mintNewAddressBook.js b/src/mintNewAddressBook.js index ed1a3d5..c116e84 100644 --- a/src/mintNewAddressBook.js +++ b/src/mintNewAddressBook.js @@ -1,6 +1,8 @@ import * as UI from 'solid-ui' import { solidLogicSingleton } from 'solid-logic' import * as $rdf from 'rdflib' +import { complain } from './localUtils' +import * as debug from './debug' const { setACLUserPublic } = solidLogicSingleton.acl // const mime = require('mime-types') @@ -17,7 +19,7 @@ export function mintNewAddressBook (dataBrowserContext, context) { UI.login.ensureLoadedProfile(context).then( context => { // 20180713 - console.log('Logged in as ' + context.me) + debug.log('Logged in as ' + context.me) const me = context.me const dom = context.dom @@ -53,10 +55,6 @@ export function mintNewAddressBook (dataBrowserContext, context) { } const appInstanceNoun = 'address book' - function complain (message) { - div.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) - } - let bookContents = `@prefix vcard: . @prefix ab: . @prefix dc: . @@ -94,9 +92,9 @@ export function mintNewAddressBook (dataBrowserContext, context) { function claimSuccess (newAppInstance, appInstanceNoun) { // @@ delete or grey other stuff - console.log(`New ${appInstanceNoun} created at ${newAppInstance}`) + debug.log(`New ${appInstanceNoun} created at ${newAppInstance}`) const p = div.appendChild(dom.createElement('p')) - p.setAttribute('style', 'font-size: 140%;') + p.classList.add('claimSuccess') p.innerHTML = 'Your [v, k])) +const mime = { + extension: (contentType) => mimeMap[contentType] || false, + lookup: (filename) => { + const ext = filename.split('.').pop().toLowerCase() + return extMap[ext] || false + } +} const ns = UI.ns const utils = UI.utils @@ -14,7 +39,7 @@ const kb = store */ export function renderMugshotGallery (dom, subject) { function complain (message) { - console.log(message) + debug.log(message) galleryDiv.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) } @@ -30,7 +55,7 @@ export function renderMugshotGallery (dom, subject) { } } catch (err) { const msg = ' Write back image link FAIL ' + pic + ', Error: ' + err - console.log(msg) + debug.log(msg) alert(msg) } } @@ -38,13 +63,13 @@ export function renderMugshotGallery (dom, subject) { function handleDroppedThing (thing) { kb.fetcher.nowOrWhenFetched(thing.doc(), function (ok, mess) { if (!ok) { - console.log('Error looking up dropped thing ' + thing + ': ' + mess) + debug.log('Error looking up dropped thing ' + thing + ': ' + mess) } else { const types = kb.findTypeURIs(thing) for (const ty in types) { - console.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped? + debug.log(' drop object type includes: ' + ty) // @@ Allow email addresses and phone numbers to be dropped? } - console.log('Default: assume web page ' + thing) // icon was: UI.icons.iconBase + 'noun_25830.svg' + debug.log('Default: assume web page ' + thing) // icon was: UI.icons.iconBase + 'noun_25830.svg' kb.add(subject, ns.wf('attachment'), thing, subject.doc()) // @@ refresh UI } @@ -56,7 +81,7 @@ export function renderMugshotGallery (dom, subject) { const extension = mime.extension(contentType) if (contentType !== mime.lookup(filename)) { filename += '_.' + extension - console.log('MIME TYPE MISMATCH -- adding extension: ' + filename) + debug.log('MIME TYPE MISMATCH -- adding extension: ' + filename) } let prefix, predicate const isImage = contentType.startsWith('image') @@ -77,7 +102,7 @@ export function renderMugshotGallery (dom, subject) { } filename = prefix + n + '.' + extension } - console.log( + debug.log( 'Putting ' + data.byteLength + ' bytes of ' + @@ -95,7 +120,7 @@ export function renderMugshotGallery (dom, subject) { complain('Error uploading ' + pic + ':' + response.status) return } - console.log(' Upload: put OK: ' + pic) + debug.log(' Upload: put OK: ' + pic) kb.add(subject, predicate, pic, subject.doc()) kb.fetcher .putBack(subject.doc(), { contentType: 'text/turtle' }) @@ -106,7 +131,7 @@ export function renderMugshotGallery (dom, subject) { } }, function (err) { - console.log( + debug.log( ' Write back image link FAIL ' + pic + ', Error: ' + err ) } @@ -118,7 +143,7 @@ export function renderMugshotGallery (dom, subject) { async function handleURIsDroppedOnMugshot (uris) { for (const u of uris) { let thing = $rdf.sym(u) // Attachment needs text label to disinguish I think not icon. - console.log('Dropped on mugshot thing ' + thing) // icon was: UI.icons.iconBase + 'noun_25830.svg' + debug.log('Dropped on mugshot thing ' + thing) // icon was: UI.icons.iconBase + 'noun_25830.svg' if (u.startsWith('http') && u.indexOf('#') < 0) { // Plain document // Take a copy of a photo on the web: @@ -160,7 +185,7 @@ export function renderMugshotGallery (dom, subject) { function droppedFileHandler (files) { for (let i = 0; i < files.length; i++) { const f = files[i] - console.log( + debug.log( ' contacts: Filename: ' + f.name + ', type: ' + @@ -178,7 +203,7 @@ export function renderMugshotGallery (dom, subject) { reader.onload = (function (theFile) { return function (e) { const data = e.target.result - console.log(' File read byteLength : ' + data.byteLength) + debug.log(' File read byteLength : ' + data.byteLength) const filename = encodeURIComponent(theFile.name) const contentType = theFile.type uploadFileToContact(filename, contentType, data) @@ -190,10 +215,8 @@ export function renderMugshotGallery (dom, subject) { function elementForImage (image) { const img = dom.createElement('img') - img.setAttribute( - 'style', - 'max-height: 10em; border-radius: 1em; margin: 0.7em;' - ) + img.classList.add('mugshotImage') + img.setAttribute('alt', image ? 'Contact photo' : 'Drop photo here') UI.widgets.makeDropTarget( img, handleURIsDroppedOnMugshot, @@ -248,7 +271,9 @@ export function renderMugshotGallery (dom, subject) { const button = UI.widgets.button( dom, UI.icons.iconBase + 'noun_925021.svg', - 'Drag here to delete' + 'Drag here to delete', + undefined, + { 'aria-label': 'Delete photo - drag image here' } ) async function droppedURIHandler (uris) { const images = kb @@ -260,10 +285,10 @@ export function renderMugshotGallery (dom, subject) { return } if (confirm(`Permanently DELETE image ${uri} completely?`)) { - console.log('Unlinking image file ' + uri) + debug.log('Unlinking image file ' + uri) await linkToPicture(subject, kb.sym(uri), true) try { - console.log('Deleting image file ' + uri) + debug.log('Deleting image file ' + uri) await kb.fetcher.webOperation('DELETE', uri) } catch (err) { alert('Unable to delete picture! ' + err) @@ -291,7 +316,7 @@ export function renderMugshotGallery (dom, subject) { UI.widgets.fileUploadButtonDiv(dom, droppedFileHandler) ) } catch (e) { - console.log('ignore fileUploadButtonDiv error for now', e) + debug.log('ignore fileUploadButtonDiv error for now', e) } right.appendChild(trashCan()) return imageToolTable diff --git a/src/ontology/forms.ttl b/src/ontology/forms.ttl deleted file mode 100644 index f803e4d..0000000 --- a/src/ontology/forms.ttl +++ /dev/null @@ -1,313 +0,0 @@ -# This turtle file defined the forms usied in the contacts management -# -# Indivduals and orgs are in one file as they both -# share some forms (address etc) and also interact (roles) - -# Now hand-edited, was originally made using form editor. -# Forms documentaion: https://solid.github.io/solid-ui/Documentation/forms-intro.html - -@prefix rdf: . -@prefix dct: . -@prefix foaf: . -@prefix owl: . -@prefix prov: . -@prefix ui: . -@prefix schema: . -@prefix solid: . -@prefix vcard: . -@prefix : <#>. - -# Ontology additions or interpretations needed for the form to work well - -# The ontology file doesn't make them disjoint. This makes the selector be a choice. -vcard:TelephoneType owl:disjointUnionOf ( vcard:Cell vcard:Home vcard:Work) . -vcard:Type owl:disjointUnionOf (vcard:Home vcard:Work) . # for email - -# Better field labels -vcard:Cell ui:label "mobile"@en . # app will imake nitial caps if nec -vcard:hasAddress ui:label "address"@en . -vcard:bday ui:label "born"@en. -vcard:hasEmail ui:label "email"@en . -vcard:hasTelephone ui:label "phone"@en . -vcard:note ui:label "notes"@en . - - - -# Ontology data to drive a top classifier - -foaf:Agent owl:disjointUnionOf ( - vcard:Individual - # @@ schema:robot @@ schem:SoftwareApplication, n0:Agent, n0:Person, - prov:SoftwareAgent - # vcard:Group a group of agents is not (currently) an agent itself. - # You can't get a decision out of it so it can't own a bank account - vcard:Organization -). - - -# Ontology data to drive the classifier - -solid:InterestingOrganization owl:disjointUnionOf ( -# Airline - a Corpration -# Consortium - a Corporation or a NGO - schema:Corporation - schema:EducationalOrganization - schema:ResearchOrganization -# FundingScheme - eh? - schema:GovernmentOrganization -# LibrarySystem -# LocalBusiness - Corporation - schema:MedicalOrganization - schema:NGO - # NewsMediaOrganization - a Corporation or a NGO - schema:MusicGroup # e.g. a band - schema:Project # like Solid - schema:SportsOrganization # a Team - ) . - -######################################################### -# The forms themselves - -foaf:Agent ui:creationForm :AgentForm . - -:AgentForm a ui:Form; schema:name "Form for editing a agent" ; - ui:parts ( :AgentClassifier :AgentOptions ) . - - :AgentClassifier a ui:Classifier; ui:label "What sort of agent?"@en; - ui:category foaf:Agent . - -:AgentOptions a ui:Options; ui:dependingon rdf:type; - ui:case [ ui:for vcard:Individual; ui:use :form1 ], - [ ui:for vcard:Organization; ui:use :orgDetailsForm ], - [ ui:for prov:SoftwareAgent; ui:use :robotDetailsForm ] . - -# Robots - -:robotDetailsForm a ui:Form; ui:parts ( ) . # @@@@ - -# Individual-specific form - -vcard:Individual - ui:creationForm :form1 . - -# The addressComment, etc., fields with a comment before each type of field -# were originally partly because the labels on the fields were clumsy like "hasAddress". -# This is fixed by adding the ui:label to the properties above, so let's try -# removing the little micro-headings - -# For org: -:orgDetailsForm a ui:Form ; dct:title "Contact details for an organozation"; - ui:parts ( - :OrgClassifier - - :fullNameField - :addresses - :eMails - :telephones - :noteField ) . -# For individual: -:form1 - dct:title "Contact Details for a person" ; - a ui:Form ; - ui:part - :fullNameField, :roleField, :orgNameField, - # :addressesComment, - :addresses, - # :emailComment, - :eMails, -# :telephoneComment, - :telephones, -# :noteComment, - :noteField ; - ui:parts ( - :fullNameField :roleField :orgNameField - # :addressesComment - :addresses - # :emailComment - :eMails - # :telephoneComment - :telephones :birthdayField - # :noteComment - :noteField ) . - - :fullNameField - a ui:SingleLineTextField ; - ui:label "Name"; - ui:maxLength "128" ; - ui:property vcard:fn ; - ui:size "40" . - - :roleField - a ui:SingleLineTextField ; - ui:suppressEmptyUneditable true; - ui:maxLength "128" ; - ui:property vcard:role ; - ui:size "40" . - - :orgNameField - a ui:SingleLineTextField ; - ui:suppressEmptyUneditable true; - ui:maxLength "128" ; - ui:property vcard:organization-name ; - ui:size "40" . - - -:addressesComment - a ui:Comment ; - ui:suppressIfUneditable true; - ui:contents "Address" . - - -:addresses - dct:title "Address details" ; - a ui:Multiple ; - ui:part :oneAddress ; - ui:property vcard:hasAddress . - -:oneAddress - a ui:Group ; - ui:parts ( :id1409437207443 :id1409437292400 :id1409437421996 :id1409437467649 :id1409437569420 ). # :id1409437646712 - -:id1409437207443 - a ui:SingleLineTextField ; - ui:maxLength "128" ; - ui:property vcard:street-address ; - ui:size "40" . - -:id1409437292400 - a ui:SingleLineTextField ; - ui:maxLength "128" ; - ui:property vcard:locality ; - ui:size "40" . - -:id1409437421996 - a ui:SingleLineTextField ; - ui:maxLength "25" ; - ui:property vcard:postal-code ; - ui:size "25" . - -:id1409437467649 - a ui:SingleLineTextField ; - ui:maxLength "128" ; - ui:property vcard:region ; - ui:size "40" . - -:id1409437569420 - a ui:SingleLineTextField ; - ui:maxLength "128" ; - ui:property vcard:country-name ; - ui:size "40" . - -:id1409437646712 - a ui:Classifier ; - ui:from rdf:Class ; - ui:property rdf:type . - - -############################## - -:emailComment - a ui:Comment ; - ui:suppressIfUneditable true; - - ui:contents "Email" . - - -:eMails - a ui:Multiple ; - ui:part :oneEMail ; - ui:property vcard:hasEmail . - -:oneEMail - a ui:Group ; # hint: side by side is good - ui:part :emailValue, :emailType ; - ui:parts ( :emailType :emailValue ). - -:emailValue - a ui:EmailField ; ui:label "email"; - ui:property vcard:value ; - ui:size "50" . - -:emailType - a ui:Classifier ; - ui:canMintNew "0" ; - ui:category vcard:Type ; - ui:from vcard:Type ; - ui:property rdf:type . - - -############################## - -:telephoneComment - a ui:Comment ; - ui:suppressIfUneditable true; - ui:contents "Phones" . - - -:telephones - a ui:Multiple ; - ui:part :onetelephone ; - ui:property vcard:hasTelephone . - -:onetelephone - a ui:Group ; - ui:part :telephoneValue, :telephoneType ; - ui:parts ( :telephoneType :telephoneValue ). - -:telephoneValue - a ui:PhoneField ; - ui:property vcard:value ; - ui:size "50" . - -:telephoneType - a ui:Classifier ; - ui:canMintNew "0" ; - ui:category vcard:TelephoneType ; - ui:from vcard:Type ; - ui:property rdf:type . - -############################## - -:birthdayField - a ui:DateField; - ui:label "Born"; - ui:suppressEmptyUneditable true; - ui:property vcard:bday . - -############################## - -:noteComment - a ui:Comment ; - ui:suppressIfUneditable true; - ui:contents "General Notes" . - -:noteField - a ui:MultiLineTextField ; - ui:suppressEmptyUneditable true; - - ui:property vcard:note . - - -############ organization forms - -:OrganinizatioCreationForm a ui:Form; schema:name "Form for editing contact details of an organization" ; - ui:parts ( :OrgClassifier :homePageURIField ) . - - -:OrgClassifier a ui:Classifier; ui:label "What sort of organization?"@en; - ui:category solid:InterestingOrganization . - - -:instituteNameField - a ui:SingleLineTextField ; - ui:label "Intitute Name"; - ui:maxLength "200" ; - ui:property schema:name ; - ui:size "80" . - - :homePageURIField a ui:NamedNodeURIField; - ui:property schema:url . # @@ ?? - - :initituteTypeField a ui:Classifier; - ui:label "What sort of organization"; - ui:category solid:InterestingOrganization . diff --git a/src/ontology/individualAndOrganizationForm.ttl b/src/ontology/individualAndOrganizationForm.ttl new file mode 100644 index 0000000..c54705a --- /dev/null +++ b/src/ontology/individualAndOrganizationForm.ttl @@ -0,0 +1,194 @@ +# This turtle file defines the forms used in the contacts management +# +# Individuals and orgs are in one file as they both +# share some forms (address etc) and also interactions (roles) + +# Forms documentation: https://solidos.github.io/solid-ui/docs/forms-intro.html + +@prefix rdf: . +@prefix dct: . +@prefix foaf: . +@prefix owl: . +@prefix prov: . +@prefix ui: . +@prefix schema: . +@prefix solid: . +@prefix vcard: . +@prefix : <#>. + + +############# For individual: +:individualForm a ui:Form ; + dct:title "Contact details for a person" ; + ui:parts ( + :fullNameField + :roleField + :orgNameField + # :addressesComment + :addresses + # :emailComment + :eMails + # :telephoneComment + :telephones + :birthdayField + # :noteComment + :noteField + ) . + + :fullNameField + a ui:SingleLineTextField ; + ui:label "Name"; + ui:maxLength "128" ; + ui:property vcard:fn . + + :roleField + a ui:SingleLineTextField ; + ui:suppressEmptyUneditable true; + ui:maxLength "128" ; + ui:property vcard:role . + + :orgNameField + a ui:SingleLineTextField ; + ui:suppressEmptyUneditable true; + ui:maxLength "128" ; + ui:property vcard:organization-name . + + :addresses a ui:Multiple ; + dct:title "Address details" ; + ui:part :oneAddress ; + ui:property vcard:hasAddress . + + :oneAddress a ui:Group ; + ui:parts ( + :id1409437207443 :id1409437292400 + :id1409437421996 :id1409437467649 + :id1409437569420 ). + + :id1409437207443 + a ui:SingleLineTextField ; + ui:maxLength "128" ; + ui:property vcard:street-address . + + :id1409437292400 + a ui:SingleLineTextField ; + ui:maxLength "128" ; + ui:property vcard:locality . + + :id1409437421996 + a ui:SingleLineTextField ; + ui:maxLength "25" ; + ui:property vcard:postal-code . + + :id1409437467649 + a ui:SingleLineTextField ; + ui:maxLength "128" ; + ui:property vcard:region . + + :id1409437569420 + a ui:SingleLineTextField ; + ui:maxLength "128" ; + ui:property vcard:country-name . + + + :eMails a ui:Multiple ; + ui:part :oneEMail ; + ui:property vcard:hasEmail . + + :oneEMail a ui:Group ; # hint: side by side is good + ui:part :emailValue, :emailType ; + ui:parts ( :emailType :emailValue ). + + :emailValue + a ui:EmailField ; ui:label "email"; + ui:property vcard:value . + + :emailType + a ui:Classifier ; + ui:canMintNew "0" ; + ui:category vcard:Type ; + ui:from vcard:Type ; + ui:property rdf:type . + + :telephones a ui:Multiple ; + ui:part :onetelephone ; + ui:property vcard:hasTelephone . + + :onetelephone + a ui:Group ; + ui:part :telephoneValue, :telephoneType ; + ui:parts ( :telephoneType :telephoneValue ). + + :telephoneValue + a ui:PhoneField ; + ui:property vcard:value . + + :telephoneType + a ui:Classifier ; + ui:canMintNew "0" ; + ui:category vcard:TelephoneType ; + ui:from vcard:Type ; + ui:property rdf:type . + + :birthdayField a ui:DateField; + ui:label "Born"@en; + ui:suppressEmptyUneditable true; + ui:property vcard:bday . + + :noteField + a ui:MultiLineTextField ; + ui:suppressEmptyUneditable true; + ui:property vcard:note . + +# Ontology additions or interpretations needed for the form to work well + +# The ontology file doesn't make them disjoint. This makes the selector be a choice. +vcard:TelephoneType owl:disjointUnionOf ( vcard:Cell vcard:Home vcard:Work) . +vcard:Type owl:disjointUnionOf (vcard:Home vcard:Work) . # for email + +# Better field labels +vcard:Cell ui:label "mobile"@en . # app will make initial caps if necessary +vcard:hasAddress ui:label "address"@en . +vcard:bday ui:label "born"@en. +vcard:hasEmail ui:label "email"@en . +vcard:hasTelephone ui:label "phone"@en . +vcard:note ui:label "notes"@en . + + +############ For organizations + +:organizationForm a ui:Form ; + dct:title "Contact details for an organization"; + ui:parts ( + :OrgClassifier + :fullNameField + :homePageURIField + :addresses + :eMails + :telephones + :noteField ) . + + :OrgClassifier a ui:Classifier; + ui:label "What sort of organization?"@en; + ui:category solid:InterestingOrganization . + + :homePageURIField a ui:NamedNodeURIField; + ui:label "Homepage"@en; + ui:property schema:url . + +# Ontology data to drive the :OrgClassifier classifier + solid:InterestingOrganization owl:disjointUnionOf ( + # Airline - a Corporation + # Consortium - a Corporation or a NGO + schema:Corporation + schema:EducationalOrganization + schema:ResearchOrganization # Proposed. https://github.com/schemaorg/schemaorg/issues/2877 + # FundingScheme - eh? + schema:GovernmentOrganization + # LibrarySystem + # LocalBusiness - Corporation + # MedicalOrganization - a Corporation or a NGO + schema:NGO + # NewsMediaOrganization - a Corporation or a NGO + schema:PerformingGroup # a band + schema:Project # like Solid + schema:SportsOrganization) . diff --git a/src/ontology/organizationForm.ttl b/src/ontology/organizationForm.ttl deleted file mode 100644 index 8b13eb6..0000000 --- a/src/ontology/organizationForm.ttl +++ /dev/null @@ -1,59 +0,0 @@ -# Form to record episodes in the life of a person -# -# - -@prefix : <#> . - -@prefix rdf: . -@prefix owl: . -@prefix schema: . -@prefix solid: . -@prefix ui: . -@prefix vcard: . -@prefix : <#>. - - vcard:Organization ui:creationForm :OrganizationCreationForm . - -# Ontology data to drive the classifier - -solid:InterestingOrganization owl:disjointUnionOf ( -# Airline - a Corporation -# Consortium - a Corporation or a NGO - schema:Corporation - schema:EducationalOrganization -# FundingScheme - eh? - schema:GovernmentOrganization -# LibrarySystem -# LocalBusiness - Corporation -# MedicalOrganization - a Corporation or a NGO - schema:NGO - # NewsMediaOrganization - a Corporation or a NGO -schema:PerformingGroup # a band -schema:Project # like Solid -schema:SportsOrganization # a Team - ) . - - :OrganizationCreationForm a ui:Form; schema:name "Form for editing an Organization" ; - ui:parts ( :OrgClassifier :homePageURIField ) . - - - :OrgClassifier a ui:Classifier; ui:label "What sort of organization?"@en; - ui:category solid:InterestingOrganization . - - - :instituteNameField - a ui:SingleLineTextField ; - ui:label "Intitute Name"; - ui:maxLength "200" ; - ui:property schema:name ; - ui:size "80" . - - :homePageURIField a ui:NamedNodeURIField; - ui:property schema:url . # @@ ?? - - :initituteTypeField a ui:Classifier; - ui:label "What sort of organization"; - ui:category solid:InterestingOrganization . - - -# ends diff --git a/src/publicData.ts b/src/publicData.ts deleted file mode 100644 index 4651210..0000000 --- a/src/publicData.ts +++ /dev/null @@ -1,403 +0,0 @@ -/* Logic to access public data stores -* -* including filtering resut by natural language etc -*/ -import { Literal, NamedNode, parse } from 'rdflib' -import { store } from 'solid-logic' -import { ns } from 'solid-ui' -import instituteDetailsQuery from './instituteDetailsQuery.sparql' - -export const AUTOCOMPLETE_LIMIT = 3000 // How many to get from server - -const subjectRegexp = /\$\(subject\)/g - -interface Term { - type: string; - value: string -} - -interface Binding { - subject: Term; - name?: Term - location?: Term - coordinates?: Term -} - -type Bindings = Binding[] - -export type QueryParameters = -{ - label: string; - logo: string; - searchByNameQuery?: string; - searchByNameURI?: string; - insitituteDetailsQuery?: string; - endpoint?: string; - class: object -} - -// Schema.org seems to suggest NGOs are non-profit and Corporaions are for-profit -// but doesn't have explicit classes -export const wikidataClasses = { - Corporation: 'http://www.wikidata.org/entity/Q6881511', // Enterprise is for-profit - EducationalOrganization: 'http://www.wikidata.org/entity/Q178706', // insitution - GovernmentOrganization: 'http://www.wikidata.org/entity/Q327333', // government agency - MedicalOrganization: 'http://www.wikidata.org/entity/Q4287745', - MusicGroup: 'http://www.wikidata.org/entity/Q32178211', // music organization - NGO: 'http://www.wikidata.org/entity/Q163740', // nonprofit organization @@ - Occupation: 'http://www.wikidata.org/entity/Q28640', // Profession - // Organization: 'http://www.wikidata.org/entity/Q43229', - Project: 'http://www.wikidata.org/entity/Q170584', - SportsOrganization: 'http://www.wikidata.org/entity/Q4438121', -} - -export async function getPreferredLanguages () { - return ['fr', 'en', 'de', 'it'] // @@ testing only -- code me later -} -export const escoParameters:QueryParameters = { - label: 'ESCO', - logo: 'https://ec.europa.eu/esco/portal/static_resource2/images/logo/logo_en.gif', - searchByNameQuery: undefined, // No sparql endpoint - searchByNameURI: 'https://ec.europa.eu/esco/api/search?language=$(language)&type=occupation&text=$(name)', - endpoint: undefined, - class: {} -} - -export const dbpediaParameters:QueryParameters = { - label: 'DBPedia', - logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/73/DBpediaLogo.svg/263px-DBpediaLogo.svg.png', - searchByNameQuery: `select distinct ?subject, ?name where { - ?subject a $(class); rdfs:label ?name - FILTER regex(?name, "$(name)", "i") - } LIMIT $(limit)`, - endpoint: 'https://dbpedia.org/sparql/', - class: { AcademicInsitution: 'http://umbel.org/umbel/rc/EducationalOrganization' } -} - -export const wikidataParameters = { - label: 'WikiData', - logo: 'https://www.wikimedia.org/static/images/project-logos/wikidatawiki.png', - endpoint: 'https://query.wikidata.org/sparql', - class: { - AcademicInsitution: 'http://www.wikidata.org/entity/Q4671277', - Enterprise: 'http://www.wikidata.org/entity/Q6881511', - Business: 'http://www.wikidata.org/entity/Q4830453', - NGO: 'http://www.wikidata.org/entity/Q79913', - CharitableOrganization: 'http://www.wikidata.org/entity/Q708676', - Insitute: 'http://www.wikidata.org/entity/Q1664720', - }, - searchByNameQuery: `SELECT ?subject ?name - WHERE { - ?klass wdt:P279* $(class) . - ?subject wdt:P31 ?klass . - ?subject rdfs:label ?name. - FILTER regex(?name, "$(name)", "i") - } LIMIT $(limit) `, // was SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en" } - - insitituteDetailsQuery: `CONSTRUCT -{ wd:Q49108 schema:name ?itemLabel; - schema:logo ?logo; - schema:logo ?sealImage; - schema:subOrganization ?subsidiary . - ?subsidiary schema:name ?subsidiaryLabel . -} -WHERE -{ - wd:Q49108 # rdfs:label ?itemLabel ; - wdt:P154 ?logo; - wdt:P158 ?sealImage ; - wdt:P355 ?subsidiary . - # ?subsidiary rdfs:label ?subsidiaryLabel . - - SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE], fr". } -}` -} - -/* From an array of bindings with a names for each row, - * remove dupliacte names for the same thing, leaving the user's - * preferred language version -*/ -export function filterByLanguage (bindings: Bindings, languagePrefs: string[]): Bindings { - const uris: Record = {} - bindings.forEach(binding => { // Organize names by their subject - const uri = binding.subject.value - uris[uri] = uris[uri] || [] - uris[uri].push(binding) - }) - - const languagePrefs2 = languagePrefs - languagePrefs2.reverse() // prefered last - - const slimmed: Binding[] = [] - for (const u in uris) { // needs hasOwnProperty ? - const bindings = uris[u] - const sortMe = bindings.map(binding => { - return [languagePrefs2.indexOf(binding.name?.['xml:lang'] || ''), binding] - }) - sortMe.sort() // best at th ebottom - sortMe.reverse() // best at the top - slimmed.push(sortMe[0][1] as Binding) - } // map u - console.log(` Filter by language: ${bindings.length} -> ${slimmed.length}`) - return slimmed -} - -export var wikidataClassMap = { - 'http://www.wikidata.org/entity/Q15936437': ns.schema('CollegeOrUniversity'), // research university - 'http://www.wikidata.org/entity/Q1664720': ns.schema('EducationalOrganization'), // insitute @@ - 'http://www.wikidata.org/entity/Q43229': ns.schema('Organization'), // research university - 'http://www.wikidata.org/entity/Q3918': ns.schema('CollegeOrUniversity'), // university - 'http://www.wikidata.org/entity/Q170584': ns.schema('Project'), // university - 'http://www.wikidata.org/entity/Q327333': ns.schema('GovernmentOrganization'), // gobvt agency - 'http://www.wikidata.org/entity/Q2221906': ns.schema('Place'), // geographic location - -} -export var predMap = { // allow other mappings top added in theory - class: ns.rdf('type'), - // logo: ns.schema('logo'), - sealImage: ns.schema('logo'), - // image: ns.schema('image'), defaults to shema - shortName: ns.foaf('nick'), - subsidiary: ns.schema('subOrganization') -} - -export function loadFromBindings (kb, solidSubject:NamedNode, bindings, doc) { - const results = {} - console.log(`loadFromBindings: subject: ${solidSubject}`) - console.log(` doc: ${doc}`) - bindings.forEach(binding => { - for (const key in binding) { - const result = binding[key] - const combined = JSON.stringify(result) // ( result.type, result.value ) - results[key] = results[key] || new Set() - results[key].add(combined) // remove duplicates - } - }) - for (const key in results) { - const values = results[key] - console.log(` results ${key} -> ${values}`) - values.forEach(combined => { - const result = JSON.parse(combined) - const { type, value } = result - let obj - if (type === 'uri') { - obj = kb.sym(value) - } else if (type === 'literal') { - obj = new Literal(value, result.language, result.datatype) - } else { - throw new Error(`loadFromBindings: unexpected type: ${type}`) - } - if (key === 'type') { - if (wikidataClassMap[value]) { - obj = wikidataClassMap[value] - } else { - console.warn('Unmapped Wikidata Class: ' + value) - } - } else if (key === 'coordinates') { - // const latlong = value // Like 'Point(-71.106111111 42.375)' - console.log(' @@@ hey a point: ' + value) - // eslint-disable-next-line no-useless-escape - const regexp = /.*\(([-0-9\.-]*) ([-0-9\.-]*)\)/ - const match = regexp.exec(value) - if (match) { - const float = ns.xsd('float') - const latitude = new Literal(match[1], null, float) - const longitude = new Literal(match[2], null, float) - kb.add(solidSubject, ns.schema('longitude'), longitude, doc) - kb.add(solidSubject, ns.schema('latitude'), latitude, doc) - } - } else if (predMap[key]) { - const pred = predMap[key] || ns.schema(key) // fallback to just using schema.org - kb.add(solidSubject, pred, obj, doc) // @@ deal with non-string and objects - console.log(` public data ${pred} ${obj}.`) - } - }) - } -} - -/* ESCO sopecific -*/ - -/* Query all entities of given class and partially matching name -*/ -export async function queryESCODataByName (filter: string, theClass:NamedNode, queryTarget: QueryParameters): Promise { - if (!queryTarget.searchByNameURI) { - throw new Error('Query target searchByNameURI is required for ESCO queries') - } - const queryURI = queryTarget.searchByNameURI - .replace('$(name)', filter) - .replace('$(limit)', '' + AUTOCOMPLETE_LIMIT) - .replace('$(class)', theClass.uri) - console.log('Querying ESCO data - uri: ' + queryURI) - - const options = { - credentials: 'omit' as const, - headers: { Accept: 'application/json' } - } // CORS - const response = await store.fetcher.webOperation('GET', queryURI, options) - // complain('Error querying db of organizations: ' + err) - const text = response.responseText - if (!text) throw new Error('No response text from ESCO query ' + queryURI) - console.log(' Query result text' + text.slice(0, 500) + '...') - if (text.length === 0) throw new Error('Wot no text back from ESCO query ' + queryURI) - const json = JSON.parse(text) - console.log(' Query result JSON' + JSON.stringify(json, null, 4).slice(0, 500) + '...') - - const results = json._embedded.results // Array - const bindings = results.map(result => { - const name = result.title - const uri = result.uri // like http://data.europa.eu/esco/occupation/57af9090-55b4-4911-b2d0-86db01c00b02 - return { name: { value: name, type: 'literal' }, uri: { type: 'IRI', value: uri } } // simulate SPARQL bindings - }) - return bindings - // return queryPublicDataSelect(sparql, queryTarget) -} - -/* Query all entities of given class and partially matching name -*/ -export async function queryPublicDataByName (filter: string, theClass:NamedNode, queryTarget: QueryParameters): Promise { - const sparql = queryTarget.searchByNameQuery! - .replace('$(name)', filter) - .replace('$(limit)', '' + AUTOCOMPLETE_LIMIT) - .replace('$(class)', theClass.uri) - console.log('Querying public data - sparql: ' + sparql) - return queryPublicDataSelect(sparql, queryTarget) -} - -export async function queryPublicDataSelect (sparql: string, queryTarget: QueryParameters): Promise { - if (!queryTarget.endpoint) { - throw new Error('Query target endpoint is required') - } - const myUrlWithParams = new URL(queryTarget.endpoint!) - myUrlWithParams.searchParams.append('query', sparql) - const queryURI = myUrlWithParams.href - console.log(' queryPublicDataSelect uri: ' + queryURI) - - const options = { - credentials: 'omit' as const, - headers: { Accept: 'application/json' } - } // CORS - const response = await store.fetcher.webOperation('GET', queryURI, options) - // complain('Error querying db of organizations: ' + err) - const text = response.responseText - // console.log(' Query result text' + text.slice(0,100) + '...') - if (!text || text.length === 0) { throw new Error('Wot no text back from query ' + queryURI) } else { - const json = JSON.parse(text) - console.log(' Query result JSON' + JSON.stringify(json, null, 4).slice(0, 100) + '...') - const bindings = json.results.bindings - return bindings - } -} - -export async function queryPublicDataConstruct (sparql: string, publicId: NamedNode, queryTarget: QueryParameters): Promise { - console.log('queryPublicDataConstruct: sparql:', sparql) - if (!queryTarget.endpoint) { - throw new Error('Query target endpoint is required') - } - const myUrlWithParams = new URL(queryTarget.endpoint) - myUrlWithParams.searchParams.append('query', sparql) - const queryURI = myUrlWithParams.href - console.log(' queryPublicDataConstruct uri: ' + queryURI) - const options = { - credentials: 'omit' as const, // CORS - headers: { Accept: 'text/turtle' } - } - const response = await store.fetcher.webOperation('GET', queryURI, options) - const text = response.responseText - const report = text && text.length > 500 ? text.slice(0, 200) + ' ... ' + text.slice(-200) : text - console.log(' queryPublicDataConstruct result text:' + report) - if (!text || text.length === 0) { throw new Error('queryPublicDataConstruct: No text back from construct query:' + queryURI) } else { parse(text, store, publicId.uri, 'text/turtle') } -} - -export async function loadPublicDataThing (kb, subject: NamedNode, publicDataID: NamedNode) { - if (publicDataID.uri.startsWith('https://dbpedia.org/resource/')) { - return getDbpediaDetails(kb, subject, publicDataID) - } else if (publicDataID.uri.match(/^https?:\/\/www\.wikidata\.org\/entity\/.*/)) { - // Previous approach: - // const QId = publicDataID.uri.split('/')[4] - // const dataURI = `http://www.wikidata.org/wiki/Special:EntityData/${QId}.ttl` - // Not used because loading the data URI gives much too much irrelevant data from Wikidata. - await getWikidataDetails(kb, subject, publicDataID) - // await getWikidataLocation(kb, subject, publicDataID) -- should get that in the details query now - } else { - const iDToFetch = publicDataID.uri.startsWith('http:') - ? kb.sym('https:' + publicDataID.uri.slice(5)) - : publicDataID - return kb.fetcher.load(iDToFetch, { - credentials: 'omit', - headers: { Accept: 'text/turtle' } - }) - } -} - -export async function getWikidataDetails (kb, solidSubject:NamedNode, publicDataID:NamedNode) { - const subjRegexp = /wd:Q49108/g - const sparql = instituteDetailsQuery.replace(subjRegexp, publicDataID.value) - await queryPublicDataConstruct(sparql, publicDataID, wikidataParameters) - console.log('getWikidataDetails: loaded.', publicDataID) -} - -export async function getWikidataDetailsOld (kb, solidSubject:NamedNode, publicDataID:NamedNode) { - const sparql = `select distinct * where { - optional { $(subject) wdt:P31 ?class } # instance of - optional { $(subject) wdt:P154 ?logo } - optional { $(subject) wdt:P158 ?sealImage } -# optional { $(subject) wdt:P159 ?headquartersLocation } - -optional { $(subject) wdt:P17 ?country } -optional { $(subject) wdt:P18 ?image } -optional { $(subject) wdt:P1813 ?shortName } - -optional { $(subject) wdt:P355 ?subsidiary } -# SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en,de,it" } -}` - .replace(subjectRegexp, publicDataID.uri) - const bindings = await queryPublicDataSelect(sparql, wikidataParameters) - loadFromBindings(kb, publicDataID, bindings, publicDataID.doc()) // arg2 was solidSubject -} - -export async function getWikidataLocation (kb, solidSubject:NamedNode, publicDataID:NamedNode) { - const sparql = `select distinct * where { - - $(subject) wdt:P276 ?location . - - optional { ?location wdt:P2044 ?elevation } - optional { ?location wdt:P131 ?region } - optional { ?location wdt:P625 ?coordinates } -optional { ?location wdt:P17 ?country } - -# SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en,de,it" } -}`.replace(subjectRegexp, publicDataID.uri) - console.log(' location query sparql:' + sparql) - const bindings = await queryPublicDataSelect(sparql, wikidataParameters) - console.log(' location query bindings:', bindings) - loadFromBindings(kb, publicDataID, bindings, publicDataID.doc()) // was solidSubject -} - -export async function getDbpediaDetails (kb, solidSubject:NamedNode, publicDataID:NamedNode) { -// Note below the string form of the named node with <> works in SPARQL - const sparql = `select distinct ?city, ?state, ?country, ?homepage, ?logo, ?lat, ?long, WHERE { - OPTIONAL { <${publicDataID}> ?city } - OPTIONAL { ${publicDataID} ?state } - OPTIONAL { ${publicDataID} ?country } - OPTIONAL { ${publicDataID} foaf:homepage ?homepage } - OPTIONAL { ${publicDataID} foaf:lat ?lat; foaf:long ?long } - OPTIONAL { ${publicDataID} ?country } - }` - /* - const predMap = { - city: ns.vcard('locality'), - state: ns.vcard('region'), - country: ns.vcard('country-name'), - homepage: ns.foaf('homepage'), - lat: ns.geo('latitude'), - long: ns.geo('longitude'), - } - */ - const bindings = await queryPublicDataSelect(sparql, dbpediaParameters) - return bindings.map(binding => { - const uri = binding.subject.value // @@ To be written - const name = binding.name?.value - return { uri, name } - }) -} diff --git a/src/rdfFormsHelper.js b/src/rdfFormsHelper.js new file mode 100644 index 0000000..b4fec10 --- /dev/null +++ b/src/rdfFormsHelper.js @@ -0,0 +1,50 @@ +import { sym, Namespace, parse } from 'rdflib' +import { widgets } from 'solid-ui' + +const baseUri = 'https://solidos.github.io/contacts-pane/src/ontology/' + +export function renderForm ( + div, + subject, // Represents the RDF that fills the form + formSource, // The imported form Turtle source + formName, // The name of the form file (e.g., 'socialMedia.ttl') + store, + dom, + editableProfile, + whichForm) { + // --- Form resource setup --- + const formUri = baseUri + formName // Full URI to the form file + const exactForm = whichForm || 'this' // If there are more 'a ui:Form' elements in a form file + const formThis = Namespace(formUri + '#')(exactForm) // NamedNode for #this in the form + + loadDocument(store, formSource, formName, formUri) + + widgets.appendForm( + dom, + div, + {}, + subject, + formThis, + editableProfile, + (ok, mes) => { + if (!ok) widgets.errorMessageBlock(dom, mes) + } + ) +} // renderForm + +// we need to load into the store some additional information about Social Media accounts +export function loadDocument ( + store, + documentSource, + documentName, + documentURI +) { + const finalDocumentUri = documentURI || baseUri + documentName // Full URI to the file + const document = sym(finalDocumentUri) // rdflib NamedNode for the document + + if (!store.holds(undefined, undefined, undefined, document)) { + // we are using the social media form because it contains the information we need + // the form can be used for both use cases: create UI for edit and render UI for display + parse(documentSource, store, finalDocumentUri, 'text/turtle', () => null) // Load doc directly + } +} diff --git a/src/styles/contactsPane.css b/src/styles/contactsPane.css new file mode 100644 index 0000000..eab336d --- /dev/null +++ b/src/styles/contactsPane.css @@ -0,0 +1,508 @@ +/* Focus indicator for keyboard navigation */ +.contactPane table tr[tabindex="0"]:focus { + outline: var(--focus-ring-width) solid var(--color-primary); + outline-offset: 2px; + background: var(--color-info-bg); +} +/* contactsPane styles — extracted from inline styles in contactsPane.js */ +/* Uses CSS custom properties from the global stylesheet (dev-global.css / mashlib) */ + +/* ── Layout: Three-column browser ────────────────────────────── */ + +.contactPane .peopleSection .selected { + background-color: var(--color-info-bg) !important; +} + +.contactPane .detailSection { + flex: 1 1 400px; + min-width: 300px; + box-sizing: border-box; + background: var(--color-section-bg); +} + +.contactPane .detailsSectionContent { + min-height: 200px; + padding: var(--spacing-lg); + max-width: 900px; + width: 100%; + box-sizing: border-box; +} + +.contactPane .detailsSectionContent--wide { + max-width: 900px; +} + +.contactPane .cardFooter { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-xs); + padding-top: var(--spacing-md); + margin-top: var(--spacing-md); +} + +.contactPane .detailsSectionContent { + margin: 0; +} + +/* ── Contact type chooser ───────────────────────────────────── */ + +.contactPane .contactTypeChooser { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); + max-width: 360px; +} + +.contactPane .contactTypeChooser h3 { + margin: 0 0 var(--spacing-xs) 0; + font-size: var(--font-size-lg); +} + +.contactPane .contactTypeSelect { + height: var(--min-touch-target); + border: 1px solid var(--color-border-pale); + border-radius: var(--border-radius-base); + padding: 0 var(--spacing-sm); + font-size: var(--font-size-sm); + background: var(--color-section-bg); +} + +/* ── Search ──────────────────────────────────────────────────── */ + +.contactPane .allGroupsButton { + border-radius: var(--border-radius-full) !important; + /* existing styles */ +} +.contactPane .searchInput { + height: var(--min-touch-target); + border: 1px solid var(--color-border-pale); + background-color: var(--color-section-bg); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23999' viewBox='0 0 24 24' width='20' height='20'%3E%3Cpath d='M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99c.41.41 1.09.41 1.5 0s.41-1.09 0-1.5l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: 8px center; + background-size: 20px 20px; + border-radius: var(--border-radius-base); + padding: 0 var(--spacing-sm) 0 34px; + font-size: var(--font-size-base); + width: 100%; + box-sizing: border-box; +} + +/* ── Contact toolbar (top-right link + delete) ──────────────── */ + +.contactPane .contact-toolbar { + display: flex; + justify-content: flex-end; + align-items: center; + gap: var(--spacing-sm); + padding: var(--spacing-xs) 0; +} + +.contactPane .contact-toolbar a img { + width: 1.3em; + height: 1em; + margin: 0; +} + +/* ── "All" groups button ─────────────────────────────────────── */ + +.contactPane .allGroupsButton { + margin-left: var(--spacing-md); + font-size: var(--font-size-base); +} + +.contactPane .allGroupsButton--loading { + background-color: var(--color-primary); +} + +.contactPane .allGroupsButton--active { + background-color: var(--color-primary); + color: var(--color-background); +} + +.contactPane .allGroupsButton--loaded { + background-color: var(--color-primary); +} + +/* ── Selection & visibility states ───────────────────────────── */ + +.contactPane .group-loading { +} + +.contactPane .hidden { + display: none; +} + +/* ── Mint new address book ───────────────────────────────────── */ + +.contactPane .claimSuccess { + font-size: var(--font-size-xl); +} + +.contactPane { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +.contactPane .addressBook-grid { + display: flex; + flex-wrap: wrap; + flex: 1; + min-width: 50%; + align-items: stretch; + width: 100%; + box-sizing: border-box; +} + +.contactPane .addressBookSection { + flex: 1 1 350px; + max-width: 485px; + box-sizing: border-box; +} + +@media ((min-width: 500px) and (max-width: 900px)) { + .contactPane .addressBookSection { + max-width: 900px; + } + .contactPane .addressBookSection section { + max-width: 485px; + } +} + +@media (max-width: 500px) { + .contactPane .addressBookSection { + max-width: 485px; + } +} + +/* Card Section Background */ +.contactPane .section-bg { + background: var(--color-section-bg); + padding: var(--spacing-md); + box-sizing: border-box; +} + +/* Primary Button */ +.contactPane .btn-primary { + min-height: var(--min-touch-target); + padding: var(--spacing-sm) var(--spacing-md); + border: 1px solid var(--color-primary); + border-radius: var(--border-radius-base); + background: var(--color-primary); + color: var(--color-background); + font-weight: 600; + cursor: pointer; + transition: all var(--animation-duration) ease; +} + +.contactPane .btn-primary:hover { + background: color-mix(in srgb, var(--color-primary) 90%, black); + box-shadow: 0 2px 4px rgba(124, 77, 255, 0.2); +} + +.contactPane .btn-primary:active { + box-shadow: 0 1px 2px rgba(124, 77, 255, 0.2); +} + +.contactPane .btn-primary:disabled { + opacity: var(--opacity-disabled, 0.6); + cursor: not-allowed; + transform: none; +} + +/* Secondary button */ +.contactPane .btn-secondary { + min-height: var(--min-touch-target); + padding: var(--spacing-sm) var(--spacing-md); + border: var(--border-width-thin) solid var(--color-secondary); + border-radius: var(--border-radius-base); + background: var(--color-secondary); + color: var(--color-background); + font-weight: var(--font-weight-bold); + cursor: pointer; + transition: all var(--animation-duration) ease; + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.contactPane .btn-secondary:hover { + background: color-mix(in srgb, var(--color-secondary) 85%, black); +} + +.contactPane .btn-secondary:disabled { + opacity: var(--opacity-disabled); + cursor: not-allowed; +} + +/* Action Button Focus - used by ChatWithMe, ProfileCard */ +.contactPane .action-button-focus:focus, +.contactPane .action-button-focus:focus-visible { + outline: 3px solid var(--color-primary) !important; + outline-offset: 2px !important; + box-shadow: 0 0 0 2px var(--color-background), 0 0 0 5px rgba(124, 77, 255, 0.25) !important; + z-index: 1; +} + +/* ── Button section: horizontal scrollable row ──────────────── */ + +.contactPane .buttonSection { + display: flex; + flex-wrap: nowrap; + align-items: center; + padding: var(--spacing-sm); + padding-bottom: 0; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + margin-bottom: 0; +} + +.contactPane .buttonSection::-webkit-scrollbar { + height: 6px; +} + +.contactPane .buttonSection::-webkit-scrollbar-thumb { + background: var(--color-border-pale); + border-radius: var(--border-radius-base); +} + +.contactPane .buttonSection::-webkit-scrollbar-track { + background: transparent; +} + +.contactPane .groupButtonsList { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: var(--spacing-xs); + list-style: none; +} + +.contactPane .buttonSection .groupButtonsList { + margin-left: var(--spacing-xs); + margin-right: var(--spacing-xs); + padding-left: 0; +} + +.contactPane .groupButtonsList li { + flex-shrink: 0; +} + +.contactPane .groupButtonsList button { + white-space: nowrap; + flex-shrink: 0; + min-width: max-content; + margin-left: 0; +} + +/* Groups list in details section — flexible 2-column grid */ +.contactPane .detailsSectionContent .groupButtonsList { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: var(--spacing-sm); + list-style: none; + padding: 0; + width: 100%; + box-sizing: border-box; +} + +.contactPane .detailsSectionContent .groupButtonsList li { + width: 100%; + aspect-ratio: 1 / 1; +} + +.contactPane .detailsSectionContent .groupButtonsList button { + width: 100%; + height: 100%; + text-align: center; + border-radius: var(--border-radius-base); + word-wrap: break-word; + overflow-wrap: break-word; +} + +@media (max-width: 599px) { + .contactPane .detailsSectionContent .groupButtonsList { + grid-template-columns: repeat(2, 1fr); + gap: var(--spacing-xs); + } + + .contactPane .detailsSectionContent .groupButtonsList button { + font-size: var(--font-size-sm); + border-radius: var(--border-radius-base); + } +} + +@media (min-width: 900px) { + .contactPane .detailsSectionContent .groupButtonsList { + grid-template-columns: repeat(3, 1fr); + } +} + +.contactPane .detailsSectionContent .newGroupBtn { + width: 100%; + box-sizing: border-box; + margin-top: var(--spacing-sm); +} + +.contactPane .detailsSectionContent h3 { + font-size: var(--font-size-xl); + margin-bottom: var(--spacing-sm); + padding-left: 0; +} + +/* Delete confirmation POPUP — centered overlay in details section */ +.contactPane .detailSection { + position: relative; +} + +.contactPane .webidControl .personaRow--webid td > div[style*="position: relative"], +.contactPane .detailsSectionContent .groupButtonsList li > div[style*="position: relative"], +.contactPane .detailsSectionContent .contact-toolbar > div[style*="position: relative"], +.contactPane .detailsSectionContent .group-membership-toolbar > div[style*="position: relative"] { + position: absolute !important; + top: 0 !important; + left: 0 !important; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.3); + z-index: 1000; +} +.contactPane .webidControl .personaRow--webid td > div[style*="position: relative"] > div, +.contactPane .detailsSectionContent .groupButtonsList li > div[style*="position: relative"] > div, +.contactPane .detailsSectionContent .contact-toolbar > div[style*="position: relative"] > div, +.contactPane .detailsSectionContent .group-membership-toolbar > div[style*="position: relative"] > div { + position: relative !important; + top: auto !important; + background: var(--color-background); + border-radius: var(--border-radius-full); + padding: var(--spacing-lg); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); + z-index: 1001; +} + +/* Selected state for All contacts button */ +.contactPane .allGroupsButton--selected { + background-color: var(--color-primary); + color: var(--color-background); +} + +/* ── Header section ──────────────────────────────────────────── */ + +.contactPane .headerSection { + background: var(--color-background); + padding: var(--spacing-sm); + border-top-left-radius: var(--border-radius-full); + border-top-right-radius: var(--border-radius-full); + margin-bottom: 0; +} + +.contactPane .headerSection header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0; +} + +.contactPane .headerSection h2 { + margin-bottom: 0; +} + +/* ── Dotted horizontal rule ─────────────────────────────────── */ + +.contactPane .dottedHr { + border: none; + border-top: 1px dotted var(--color-text-muted); + margin: 0; +} + +/* ── Search section ─────────────────────────────────────────── */ + +.contactPane .searchSection { + padding: var(--spacing-sm); + padding-bottom: 0; + margin-bottom: 0; +} + +/* ── People list section ────────────────────────────────────── */ + +.contactPane .peopleSection { + display: flex; + background: var(--color-background); + border-top: 1px dotted var(--color-text-muted); + margin-bottom: 0; +} + +.contactPane .peopleSection ul { + list-style: none; + padding: 0; + margin: 0; + width: 100%; + max-height: 70vh; + overflow-y: auto; +} + +.contactPane .peopleSection li { + border-top: 1px solid var(--color-border-pale); + padding: var(--spacing-xs); +} + +/* ── Person list item (addressBookPresenter) ─────────────────── */ + +.contactPane .personLi-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.contactPane .personLi-avatar { + width: 45px; + height: 45px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.contactPane .personLi-avatar .avatar-placeholder { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; +} + +.contactPane .personLi-avatar img { + width: 40px; + height: 40px; + border-radius: 50%; + object-fit: cover; +} + +.contactPane .personLi-info { + flex: 1; + margin-left: var(--spacing-sm); + overflow: hidden; +} + +.contactPane .personLi-name { + font-weight: bold; + font-size: var(--font-size-base); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.contactPane .personLi-arrow { + margin-left: auto; + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/src/styles/groupMembership.css b/src/styles/groupMembership.css new file mode 100644 index 0000000..940d129 --- /dev/null +++ b/src/styles/groupMembership.css @@ -0,0 +1,68 @@ +/* ── Group Membership Section ──────────────────────────────── */ + +.contactPane .group-membership-container { + padding: var(--spacing-sm) 0; +} + +/* Grid wrapper — matches detailsSectionContent groupButtonsList */ +.contactPane .group-pills-wrapper { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: var(--spacing-sm); + list-style: none; + padding: 0; + margin: 0; + width: 100%; +} + +/* Each group item: button on top, toolbar below */ +.contactPane .group-membership-item { + display: flex; + flex-direction: column; + align-items: stretch; + max-width: 256px; +} + +.contactPane .group-membership-item > button { + width: 100%; + text-align: center; + border-radius: var(--border-radius-base); + word-wrap: break-word; + overflow-wrap: break-word; + min-height: var(--min-touch-target); +} + +/* Toolbar with link icon and delete button below each group button */ +.contactPane .group-membership-toolbar { + display: flex; + justify-content: flex-end; + align-items: center; + gap: var(--spacing-xs); + padding: var(--spacing-xs) 0 0 0; +} + +.contactPane .group-membership-toolbar a img { + width: 1.3em; + height: 1em; + margin: 0; +} + +@media (max-width: 599px) { + .contactPane .group-pills-wrapper { + grid-template-columns: repeat(2, 1fr); + gap: var(--spacing-xs); + max-width: 100%; + } + + .contactPane .group-membership-item > button { + font-size: var(--font-size-sm); + border-radius: var(--border-radius-base); + } +} + +@media (min-width: 900px) { + .contactPane .group-pills-wrapper { + grid-template-columns: repeat(3, 1fr); + gap: var(--spacing-sm); + } +} diff --git a/src/styles/individual.css b/src/styles/individual.css new file mode 100644 index 0000000..b6c7a20 --- /dev/null +++ b/src/styles/individual.css @@ -0,0 +1,7 @@ +/* individual.js styles — extracted from inline styles */ + +/* ── Individual pane container ───────────────────────────────── */ + +.contactPane .individualPane { + padding: var(--spacing-xs) var(--spacing-lg) var(--spacing-md) var(--spacing-lg); +} \ No newline at end of file diff --git a/src/styles/mugshotGallery.css b/src/styles/mugshotGallery.css new file mode 100644 index 0000000..d6f9605 --- /dev/null +++ b/src/styles/mugshotGallery.css @@ -0,0 +1,10 @@ +/* mugshotGallery.js styles — extracted from inline styles */ +/* Uses CSS custom properties from the global stylesheet (dev-global.css / mashlib) */ + +/* ── Mugshot image ───────────────────────────────────────────── */ + +.contactPane .mugshotImage { + max-height: 10em; + border-radius: var(--border-radius-full); + margin: var(--spacing-sm); +} diff --git a/src/styles/rdfFormsEnforced.css b/src/styles/rdfFormsEnforced.css new file mode 100644 index 0000000..80e8ca4 --- /dev/null +++ b/src/styles/rdfFormsEnforced.css @@ -0,0 +1,388 @@ +/* Solid-UI form */ + +/* Vertically center autocomplete input in .formFieldValue */ +.individualPane .formFieldValue > div[style*="flex-direction: row"], +.contactPane .formFieldValue > div[style*="flex-direction: row"] { + align-items: center; + display: flex; +} + +.individualPane .formFieldValue input[data-testid="autocomplete-input"], +.contactPane .formFieldValue input[data-testid="autocomplete-input"] { + vertical-align: middle; +} + +.individualPane .hoverControl:has(> img:first-child), +.contactPane .hoverControl:has(> img:first-child) { + background-color: transparent !important; + border: none !important; + margin: 0 !important; + border-radius: 0 !important; + padding: 0.7em !important; + min-height: var(--min-touch-target); + min-width: var(--min-touch-target); + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.individualPane .hoverControlHide, +.contactPane .hoverControlHide { + width: 1.5em !important; + height: 1.5em !important; + display: block; + margin-top: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; + margin-bottom: 0.3em !important; +} + +.individualPane .hoverControl:has(> img:first-child) > span, +.contactPane .hoverControl:has(> img:first-child) > span { + display: inline-flex; + align-items: center; + margin-left: 0.3em; +} + +.individualPane div[style*="padding: 0.5em"]:has(> img), +.contactPane div[style*="padding: 0.5em"]:has(> img) { + display: inline-flex; + align-items: center; +} + +.individualPane div[style*="padding: 0.5em"]:has(> img) > span, +.contactPane div[style*="padding: 0.5em"]:has(> img) > span { + margin-left: 0.3em; +} + +.individualPane .hoverControl:has(> img:first-child):hover, +.contactPane .hoverControl:has(> img:first-child):hover { + background-color: transparent !important; +} + +.individualPane button:has(> img[src$=".svg"]), +.contactPane button:has(> img[src$=".svg"]) { + background-color: transparent !important; + border: none !important; + margin: 0 !important; + border-radius: 0 !important; +} + +.individualPane button, +.contactPane button { + min-height: var(--min-touch-target); + min-width: var(--min-touch-target); +} + +.individualPane input:not([type="color"]), +.contactPane input:not([type="color"]), +.individualPane textarea, +.contactPane textarea, +.individualPane select, +.contactPane select { + max-width: 100%; + min-width: 0; + box-sizing: border-box ; + font: inherit; + color: var(--color-text); + background-color: var(--color-card-bg) !important; + border: 1px solid var(--color-border-pale); +} + +.individualPane textarea, +.contactPane textarea { + border-color: var(--color-border-pale) !important; + margin: 0 !important; + padding: 0 !important; +} + +.individualPane input[type="date"], +.contactPane input[type="date"], +.individualPane input[type="month"], +.contactPane input[type="month"], +.individualPane input[type="week"], +.contactPane input[type="week"], +.individualPane input[type="time"], +.contactPane input[type="time"], +.individualPane input[type="datetime-local"], +.contactPane input[type="datetime-local"] { + min-height: var(--min-touch-target); +} + +.individualPane .hoverControl:has(> img:first-child):focus-visible, +.contactPane .hoverControl:has(> img:first-child):focus-visible, +.individualPane button:focus-visible, +.contactPane button:focus-visible, +.individualPane input:not([type="color"]):focus-visible, +.contactPane input:not([type="color"]):focus-visible, +.individualPane textarea:focus-visible, +.contactPane textarea:focus-visible, +.individualPane select:focus-visible, +.contactPane select:focus-visible { + outline: var(--focus-ring-width) solid var(--color-primary) !important; + outline-offset: 2px; + box-shadow: 0 0 0 1px var(--color-background); +} + +.individualPane input[type="url"], +.contactPane input[type="url"] { + width: 100%; +} + +.individualPane .formFieldValue, +.contactPane .formFieldValue { + min-width: 0; + margin-bottom: var(--spacing-sm); +} + +.individualPane .formFieldValue table, +.contactPane .formFieldValue table { + margin: 0 !important; + padding: 0 !important; +} + +.individualPane .formFieldValue td, +.contactPane .formFieldValue td { + padding: 0 !important; + vertical-align: middle; +} + +.individualPane .formFieldValue table[data-testid="autocomplete-table"], +.contactPane .formFieldValue table[data-testid="autocomplete-table"] { + height: 100%; +} + +.individualPane .formFieldValue input:not([type="color"]), +.contactPane .formFieldValue input:not([type="color"]), +.individualPane .formFieldValue textarea, +.contactPane .formFieldValue textarea, +.individualPane .formFieldValue select, +.contactPane .formFieldValue select { + width: 100%; + max-width: 100%; +} + +.individualPane select#formSelect, +.contactPane select#formSelect { + width: 100%; + max-width: 97%; + box-sizing: border-box; + margin-left: 0 !important; + margin-right: 0 !important; +} + +.individualPane span select, +.contactPane span select { + max-width: 96% !important; + box-sizing: border-box; + margin: 0 !important; +} + +.individualPane .formFieldValue span select, +.contactPane .formFieldValue span select { + margin-left: 0 !important; + margin-right: 0 !important; +} + +/* Remove border/padding from the first wrapper div (and its first child wrapper). */ +.individualPane > div:first-of-type, +.contactPane > div:first-of-type, +.individualPane > div:first-of-type > div:first-of-type, +.contactPane > div:first-of-type > div:first-of-type { + border: none !important; + padding: 0 !important; +} + +/* In contactPane, remove border/padding from all direct child divs. */ +.individualPane > div, +.contactPane > div { + border: none !important; + padding: 0 !important; +} + +/* Align schema.org, solid terms, FOAF, vCard, and org field labels with their input values. */ +.individualPane :not(.choiceBox):has(> .formFieldName):has(> .formFieldValue), +.contactPane :not(.choiceBox):has(> .formFieldName):has(> .formFieldValue) { + display: flex; + align-items: baseline; + margin-bottom: var(--spacing-sm); +} + +/* for the Resume inside corporation choice */ +/* Add space between classifierBox label and select box */ +.individualPane .choiceBox .classifierBox-label, +.contactPane .choiceBox .classifierBox-label { + margin-right: 0; + padding-left: 0.3em; +} + +.individualPane .choiceBox .choiceBox-selectBox select, +.contactPane .choiceBox .choiceBox-selectBox select { + margin-left: 2.1em !important; +} + +/* for the Resume orga details */ +/* Add space between classifierBox label and select box */ +.individualPane .classifierBox .classifierBox-label, +.contactPane .classifierBox .classifierBox-label { + margin-right: 0; + padding-left: 0.3em; + width: 8em; + padding: 0.3em; + vertical-align: middle; +} + +.individualPane .classifierBox .classifierBox-selectBox, +.contactPane .classifierBox .classifierBox-selectBox { + margin-left: 0 !important; +} + +.individualPane .classifierBox .classifierBox-selectBox select, +.contactPane .classifierBox .classifierBox-selectBox select { + margin-left: 0 !important; +} + +.individualPane .formFieldValue > span > select, +.contactPane .formFieldValue > span > select { + margin-left: 0 !important; +} + +.individualPane :not(.choiceBox):has(> .formFieldName):has(> .formFieldValue) > .formFieldValue, +.contactPane :not(.choiceBox):has(> .formFieldName):has(> .formFieldValue) > .formFieldValue { + margin-bottom: 0; +} + +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://schema.org/"]), +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://schema.org/"]), +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/solid/terms#"]), +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/solid/terms#"]), +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://xmlns.com/foaf/0.1/"]), +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://xmlns.com/foaf/0.1/"]), +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/2006/vcard/ns"]), +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/2006/vcard/ns"]), +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/org#"]), +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/org#"]) { + display: inline-flex; + align-items: center; + vertical-align: middle; +} + +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://schema.org/"]) + .formFieldValue, +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://schema.org/"]) + .formFieldValue, +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/solid/terms#"]) + .formFieldValue, +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/solid/terms#"]) + .formFieldValue, +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://xmlns.com/foaf/0.1/"]) + .formFieldValue, +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://xmlns.com/foaf/0.1/"]) + .formFieldValue, +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/2006/vcard/ns"]) + .formFieldValue, +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/2006/vcard/ns"]) + .formFieldValue, +.individualPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/org#"]) + .formFieldValue, +.contactPane :not(.choiceBox) > .formFieldName:has(a[href*="http://www.w3.org/ns/org#"]) + .formFieldValue { + display: inline-flex; + align-items: center; + vertical-align: middle; + flex: 1; + min-width: 0; +} + +.individualPane textarea, +.contactPane textarea, +.individualPane .formFieldValue textarea, +.contactPane .formFieldValue textarea { + appearance: none; + -webkit-appearance: none; + border: 0.05em solid var(--color-secondary) !important; + border-style: solid !important; + border-width: 0.05em !important; + border-color: var(--color-secondary) !important; + border-radius: var(--border-radius-base) !important; + width: 100%; + box-sizing: border-box; + margin-top: var(--spacing-xs); + margin-left: 0 !important; + margin-right: 0 !important; +} + +/* Add horizontal gap between label and textarea for all label+textarea pairs. */ +.individualPane div:has(> a) + div:has(textarea), +.contactPane div:has(> a) + div:has(textarea) { + margin-left: var(--spacing-sm); +} + +/* Center textarea label vertically in flex rows. */ +.individualPane div[style*="display: flex"][style*="flex-direction: row"]:has(textarea), +.contactPane div[style*="display: flex"][style*="flex-direction: row"]:has(textarea) { + align-items: flex-start; +} + +.individualPane div[style*="display: flex"][style*="flex-direction: row"]:has(textarea) > div:has(> a), +.contactPane div[style*="display: flex"][style*="flex-direction: row"]:has(textarea) > div:has(> a) { + padding-left: var(--spacing-xs); + padding-top: var(--spacing-sm); +} + +/* Keep autocomplete/table-based fields (e.g. Occupation) aligned to label text baseline. */ +.individualPane :not(.choiceBox):has(> .formFieldValue input[data-testid="autocomplete-input"]), +.contactPane :not(.choiceBox):has(> .formFieldValue input[data-testid="autocomplete-input"]) { + align-items: flex-start; +} + +.individualPane :not(.choiceBox):has(> .formFieldValue input[data-testid="autocomplete-input"]) > .formFieldName, +.contactPane :not(.choiceBox):has(> .formFieldValue input[data-testid="autocomplete-input"]) > .formFieldName { + padding-top: var(--spacing-xs) !important; +} + +.individualPane .formFieldValue:has(input[data-testid="autocomplete-input"]), +.contactPane .formFieldValue:has(input[data-testid="autocomplete-input"]) { + align-self: flex-start; +} + +.individualPane .formFieldValue table[data-testid="autocomplete-table"], +.contactPane .formFieldValue table[data-testid="autocomplete-table"], +.individualPane .formFieldValue input[data-testid="autocomplete-input"], +.contactPane .formFieldValue input[data-testid="autocomplete-input"] { + margin: 0 !important; +} + +.individualPane .formFieldValue table[data-testid="autocomplete-table"], +.contactPane .formFieldValue table[data-testid="autocomplete-table"] { + vertical-align: baseline; +} + +.individualPane input:not([type="color"]), +.contactPane input:not([type="color"]) { + width: 100%; + margin-left: 0 !important; + margin-right: 0 !important; +} + +.individualPane input:disabled, +.contactPane input:disabled, +.individualPane textarea:disabled, +.contactPane textarea:disabled, +.individualPane select:disabled, +.contactPane select:disabled, +.individualPane input[readonly], +.contactPane input[readonly], +.individualPane textarea[readonly], +.contactPane textarea[readonly], +.individualPane input:read-only, +.contactPane input:read-only, +.individualPane textarea:read-only, +.contactPane textarea:read-only { + background-color: var(--color-background) !important; + cursor: not-allowed; + opacity: 0.75; + border: 0.05em solid var(--color-background) !important; +} + +.individualPane textarea, +.contactPane textarea, +.individualPane .formFieldValue textarea, +.contactPane .formFieldValue textarea { + padding: var(--spacing-xs) !important; +} + +.contactPane .webidControl table td div.contactPane.namedPane { + border: none !important; +} \ No newline at end of file diff --git a/src/styles/toolsPane.css b/src/styles/toolsPane.css new file mode 100644 index 0000000..5caa3a2 --- /dev/null +++ b/src/styles/toolsPane.css @@ -0,0 +1,46 @@ +/* toolsPane.js styles — extracted from inline styles */ +/* Uses CSS custom properties from the global stylesheet (dev-global.css / mashlib) */ + +/* ── Tools pane table ────────────────────────────────────────── */ + +.contactPane .statsLog { + font-size: var(--font-size-lg); + margin: var(--spacing-md); + background-color: var(--color-background); +} + +.contactPane .statsLog pre { + padding: var(--spacing-md); + white-space: pre-wrap; + word-wrap: break-word; + overflow-wrap: break-word; + overflow: hidden; + max-width: 100%; +} + +/* ── Tools pane layout ────────────────────────────────────────── */ + +.contactPane .toolsPane { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); +} + +.contactPane .toolsButtonsContainer { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-xs); +} + +/* ── Load index button states ──────────────────────────────── */ + +.contactPane .toolsButton--loading { +} + +.contactPane .toolsButton--error { + background-color: var(--color-error); +} + +.contactPane .toolsButton--success { + background-color: var(--color-primary); +} diff --git a/src/styles/webidControl.css b/src/styles/webidControl.css new file mode 100644 index 0000000..983acc4 --- /dev/null +++ b/src/styles/webidControl.css @@ -0,0 +1,58 @@ +/* ── Named pane (rendered sub-pane) ──────────────────────────── */ +.contactPane .namedPane { + border: 0.1em solid var(--color-text-muted); + border-radius: var(--border-radius-base); +} + +/* ── Persona row ─────────────────────────────────────────────── */ + +.contactPane .personaRow { + padding: var(--spacing-xs); +} + +.contactPane .personaRow--webid { + background-color: var(--color-info-bg); +} + +/* ── Full-width elements ─────────────────────────────────────── */ + +.contactPane .fullWidth { + width: 100%; +} + +/* ── Open/close profile button ───────────────────────────────── */ + +.contactPane .personaOpenButton { + float: right; + background-color: transparent; + border: none; +} + +/* ── Section heading ─────────────────────────────────────────── */ + +.contactPane .webidHeading { + font-size: var(--font-size-lg); + font-weight: bold; + color: var(--color-primary); + padding: var(--spacing-xs); + margin: var(--spacing-sm) 0; +} + +/* ── Prompt text ─────────────────────────────────────────────── */ + +.contactPane .webidPrompt { + padding: var(--spacing-sm); + border: none; + font-size: var(--font-size-base); + white-space: pre-wrap; +} + +/* ── Visibility / display helpers ────────────────────────────── */ + +.contactPane .hidden { + display: none; +} + +.contactPane .collapsed { + visibility: collapse; +} diff --git a/src/toolsPane.js b/src/toolsPane.js index 08cc99c..9e9a9cf 100644 --- a/src/toolsPane.js +++ b/src/toolsPane.js @@ -1,772 +1,798 @@ // The tools pane is for managing and debugging and maintaining solid contacts databases -// -/* global confirm, $rdf */ - import * as UI from 'solid-ui' import { store } from 'solid-logic' import { saveNewGroup, addPersonToGroup, groupMembers } from './contactLogic' +import './styles/toolsPane.css' +import * as $rdf from 'rdflib' +import { complain, normalizeGroupUri } from './localUtils' +import * as debug from './debug' + +const kb = store +const ns = UI.ns +const VCARD = ns.vcard + +let book +let selectedGroups +let logSpace +let refreshGroupsFn export function toolsPane ( selectAllGroups, - selectedGroups, + selectedGroupsParam, groupsMainTable, - book, + bookParam, dataBrowserContext, - me + me, + refreshGroups ) { + book = bookParam + selectedGroups = selectedGroupsParam + refreshGroupsFn = refreshGroups const dom = dataBrowserContext.dom - const kb = store - const ns = UI.ns - const VCARD = ns.vcard - const buttonStyle = 'font-size: 100%; margin: 0.8em; padding:0.5em;' const pane = dom.createElement('div') - const table = pane.appendChild(dom.createElement('table')) - table.setAttribute( - 'style', - 'font-size:120%; margin: 1em; border: 0.1em #ccc ;' - ) - const headerRow = table.appendChild(dom.createElement('tr')) - headerRow.textContent = UI.utils.label(book) + ' - tools' - headerRow.setAttribute( - 'style', - 'min-width: 20em; padding: 1em; font-size: 150%; border-bottom: 0.1em solid red; margin-bottom: 2em;' - ) - - const statusRow = table.appendChild(dom.createElement('tr')) - const statusBlock = statusRow.appendChild(dom.createElement('div')) - statusBlock.setAttribute('style', 'padding: 2em;') - const MainRow = table.appendChild(dom.createElement('tr')) - const box = MainRow.appendChild(dom.createElement('table')) - table.appendChild(dom.createElement('tr')) // bottomRow - - const context = { - target: book, - me, - noun: 'address book', - div: pane, - dom, - statusRegion: statusBlock - } + pane.classList.add('toolsPane') - function complain (message) { - console.log(message) - statusBlock.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')) - } + const settingsHeader = dom.createElement('h3') + settingsHeader.textContent = 'Tools' + pane.appendChild(settingsHeader) - // Body of main pane function - async function main () { - box.appendChild( - UI.aclControl.ACLControlBox5( - book.dir(), - dataBrowserContext, - 'book', - kb, - function (ok, body) { - if (!ok) box.innerHTML = 'ACL control box Failed: ' + body - } - ) - ) + const divStatistics = pane.appendChild(dom.createElement('div')) + divStatistics.classList.add('statsLog') - // - try { - await UI.login.registrationControl(context, book, ns.vcard('AddressBook')) - } catch (e) { - UI.widgets.complain(context, 'registrationControl: ' + e) - } - console.log('Registration control finished.') + logSpace = divStatistics.appendChild(dom.createElement('pre')) + logSpace.setAttribute('id', 'logSpace') - // Output stats in line mode form - const logSpace = MainRow.appendChild(dom.createElement('pre')) - function log (message) { - console.log(message) - logSpace.textContent += message + '\n' - } + const buttonsContainer = pane.appendChild(dom.createElement('div')) + buttonsContainer.classList.add('toolsButtonsContainer') - function stats () { - const totalCards = kb.each(undefined, VCARD('inAddressBook'), book).length - log('' + totalCards + ' cards loaded. ') - let groups = kb.each(book, VCARD('includesGroup')) - const strings = new Set(groups.map(group => group.uri)) // remove dups - groups = [...strings].map(uri => kb.sym(uri)) - log('' + groups.length + ' total groups. ') - const gg = [] - for (const g in selectedGroups) { - gg.push(g) - } - log('' + gg.length + ' selected groups. ') + function setActiveButton (activeBtn) { + const wasActive = activeBtn.classList.contains('btn-primary') + buttonsContainer.querySelectorAll('button').forEach(btn => { + btn.classList.remove('btn-primary', 'toolsButton--loading', 'toolsButton--error', 'toolsButton--success') + btn.classList.add('btn-secondary') + }) + if (!wasActive) { + activeBtn.classList.remove('btn-secondary') + activeBtn.classList.add('btn-primary') } + } - async function loadIndexHandler (_event) { - loadIndexButton.setAttribute('style', 'background-color: #ffc;') - const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) - try { - await kb.fetcher.load(nameEmailIndex) - } catch (e) { - loadIndexButton.setAttribute('style', 'background-color: #fcc;') - log('Error: People index has NOT been loaded' + e + '\n') - } - loadIndexButton.setAttribute('style', 'background-color: #cfc;') - log(' People index has been loaded\n') - } // loadIndexHandler - const loadIndexButton = pane.appendChild(dom.createElement('button')) - loadIndexButton.textContent = 'Load main index' - loadIndexButton.style.cssText = buttonStyle - loadIndexButton.addEventListener('click', loadIndexHandler) - - const statButton = pane.appendChild(dom.createElement('button')) - statButton.textContent = 'Statistics' - statButton.style.cssText = buttonStyle - statButton.addEventListener('click', stats) - - const checkAccessButton = pane.appendChild(dom.createElement('button')) - checkAccessButton.textContent = - 'Check individual card access of selected groups' - checkAccessButton.style.cssText = buttonStyle - async function checkAcces (_event) { - function doCard (card) { - UI.acl.fixIndividualCardACL(card, log, function (ok, message) { - if (ok) { - log('Success for ' + UI.utils.label(card)) - } else { - log('Failure for ' + UI.utils.label(card) + ': ' + message) - } - }) - } - const gg = [] - for (const g in selectedGroups) { - gg.push(g) - } + const loadIndexButton = buttonsContainer.appendChild(dom.createElement('button')) + loadIndexButton.textContent = 'Load main index' + loadIndexButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + loadIndexButton.addEventListener('click', () => { + setActiveButton(loadIndexButton) + logSpace.textContent = '' + loadIndexHandler(loadIndexButton, logSpace) + }) + + const statButton = buttonsContainer.appendChild(dom.createElement('button')) + statButton.textContent = 'Statistics' + statButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + statButton.addEventListener('click', () => { + setActiveButton(statButton) + logSpace.textContent = '' + stats(logSpace) + }) + + const checkAccessButton = buttonsContainer.appendChild(dom.createElement('button')) + checkAccessButton.textContent = + 'Check individual contact access of selected groups' + checkAccessButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + checkAccessButton.addEventListener('click', (event) => { + setActiveButton(checkAccessButton) + logSpace.textContent = '' + checkAcces(event) + }) + + // DUPLICATES CHECK + const checkDuplicates = buttonsContainer.appendChild(dom.createElement('button')) + checkDuplicates.textContent = 'Find duplicate contacts' + checkDuplicates.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + checkDuplicates.addEventListener('click', function (_event) { + setActiveButton(checkDuplicates) + logSpace.textContent = '' + const stats = {} // global god context + + stats.book = book + stats.nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) + log(logSpace, 'Loading name index...') + + store.fetcher.nowOrWhenFetched( + stats.nameEmailIndex, + undefined, + function (_ok, _message) { + log(logSpace, 'Loaded name index.') + + stats.cards = [] + stats.duplicates = [] + stats.definitive = [] + stats.nameless = [] + + stats.exactDuplicates = [] + stats.nameOnlyDuplicates = [] + + stats.uniquesSet = [] + stats.groupProblems = [] + + // Erase one card and all its files -> (err) + // + /* + function eraseOne (card) { + return new Promise(function (resolve, reject) { + function removeFromMainIndex () { + var indexBit = kb.connectedStatements(card, stats.nameEmailIndex) + log(logSpace, 'Bits of the name index file:' + indexBit) + log(logSpace, 'Patching main index file...') + kb.updater.update(indexBit, [], function (uri, ok, body) { + if (ok) { + log(logSpace, 'Success') + resolve(null) + } else { + log(logSpace, 'Error patching index file! ' + body) + reject('Error patching index file! ' + body) + } + }) + } + var filesToDelete = [ card.doc() ] + var photos = kb.each(card, ns.vcard('hasPhoto')) // could be > 1 + if (photos.length) { + filesToDelete = filesToDelete.concat(photos) + } + filesToDelete.push(card.dir()) // the folder last + log(logSpace, 'Files to delete: ' + filesToDelete) + if (!confirm('DELETE card ' + card.dir() + ' for "' + kb.any(card, VCARD('fn')) + '", with ' + kb.each(card).length + 'statements?')) { + return resolve('Cancelled by user') + } - for (let i = 0; i < gg.length; i++) { - const g = kb.sym(gg[i]) - const a = groupMembers(kb, g) - log(UI.utils.label(g) + ': ' + a.length + ' members') - for (let j = 0; j < a.length; j++) { - const card = a[j] - log(UI.utils.label(card)) - doCard(card) - } - } - } - checkAccessButton.addEventListener('click', checkAcces) - - // /////////////////////////////////////////////////////////////////////////// - // - // DUPLICATES CHECK - const checkDuplicates = pane.appendChild(dom.createElement('button')) - checkDuplicates.textContent = 'Find duplicate cards' - checkDuplicates.style.cssText = buttonStyle - checkDuplicates.addEventListener('click', function (_event) { - const stats = {} // global god context - - stats.book = book - stats.nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) - log('Loading name index...') - - store.fetcher.nowOrWhenFetched( - stats.nameEmailIndex, - undefined, - function (_ok, _message) { - log('Loaded name index.') - - stats.cards = [] - stats.duplicates = [] - stats.definitive = [] - stats.nameless = [] - - stats.exactDuplicates = [] - stats.nameOnlyDuplicates = [] - - stats.uniquesSet = [] - stats.groupProblems = [] - - // Erase one card and all its files -> (err) - // - /* - function eraseOne (card) { - return new Promise(function (resolve, reject) { - function removeFromMainIndex () { - var indexBit = kb.connectedStatements(card, stats.nameEmailIndex) - log('Bits of the name index file:' + indexBit) - log('Patching main index file...') - kb.updater.update(indexBit, [], function (uri, ok, body) { - if (ok) { - log('Success') - resolve(null) + function deleteNextFile () { + var resource = filesToDelete.shift() + if (!resource) { + log(logSpace, 'All deleted') + removeFromMainIndex() + resolve() + } + log(logSpace, 'Deleting ... ' + resource) + kb.fetcher.delete(resource) + .then(function () { + log(logSpace, 'Deleted ok: ' + resource) + deleteNextFile() + }) + .catch(function (e) { + var err = '*** ERROR deleting ' + resource + ': ' + e + log(logSpace, err) + if (confirm('Patch out index file for card ' + card.dir() + ' EVEN THOUGH card DELETE errors?')) { + removeFromMainIndex() } else { - log('Error patching index file! ' + body) - reject('Error patching index file! ' + body) + reject(err) } }) + } + deleteNextFile() + }) // Promise + } // erase one +*/ + // Check actual records to see which are exact matches - slow + stats.nameDupLog = kb.sym(book.dir().uri + 'dedup-nameDupLog.ttl') + stats.exactDupLog = kb.sym(book.dir().uri + 'dedup-exactDupLog.ttl') + /* + function checkOne (card) { + return new Promise(function (resolve, reject) { + var name = kb.anyValue(card, ns.vcard('fn')) + var other = stats.definitive[name] + kb.fetcher.load([card, other]).then(function (xhrs) { + var exclude = {} + exclude[ns.vcard('hasUID').uri] = true + exclude[ns.dc('created').uri] = true + exclude[ns.dc('modified').uri] = true + function filtered (x) { + return kb.statementsMatching(null, null, null, x.doc()).filter(function (st) { + return !exclude[st.predicate.uri] + }) + } + var desc = filtered(card) + var desc2 = filtered(other) + // var desc = connectedStatements(card, card.doc(), exclude) + // var desc2 = connectedStatements(other, other.doc(), exclude) + if (desc.length !== desc2.length) { + log(logSpace, 'CARDS to NOT match lengths ') + stats.nameOnlyDuplicates.push(card) + return resolve(false) } - var filesToDelete = [ card.doc() ] - var photos = kb.each(card, ns.vcard('hasPhoto')) // could be > 1 - if (photos.length) { - filesToDelete = filesToDelete.concat(photos) + if (!desc.length) { + log(logSpace, '@@@@@@ Zero length ') + stats.nameOnlyDuplicates.push(card) + return resolve(false) } - filesToDelete.push(card.dir()) // the folder last - log('Files to delete: ' + filesToDelete) - if (!confirm('DELETE card ' + card.dir() + ' for "' + kb.any(card, VCARD('fn')) + '", with ' + kb.each(card).length + 'statements?')) { - return resolve('Cancelled by user') + // //////// Compare the two + // Cheat: serialize and compare + // var cardText = $rdf.serialize(card.doc(), kb, card.doc().uri, 'text/turtle') + // var otherText = $rdf.serialize(other.doc(), kb, other.doc().uri, 'text/turtle') + var cardText = (new $rdf.Serializer(kb)).setBase(card.doc().uri).statementsToN3(desc) + var otherText = (new $rdf.Serializer(kb)).setBase(other.doc().uri).statementsToN3(desc2) + // + // log('Name: ' + name + ', statements: ' + desc.length) + // log('___________________________________________') + // log('KEEPING: ' + other.doc() + '\n' + cardText) + // log('___________________________________________') + // log('DELETING: '+ card.doc() + '\n' + otherText) + // log('___________________________________________') + // + if (cardText !== otherText) { + log(logSpace, 'Texts differ') + stats.nameOnlyDuplicates.push(card) + return resolve(false) } - - function deleteNextFile () { - var resource = filesToDelete.shift() - if (!resource) { - log('All deleted') - removeFromMainIndex() - resolve() + var cardGroups = kb.each(null, ns.vcard('hasMember'), card) + var otherGroups = kb.each(null, ns.vcard('hasMember'), other) + for (var j = 0; j < cardGroups.length; j++) { + var found = false + for (var k = 0; k < otherGroups.length; k++) { + if (otherGroups[k].sameTerm(cardGroups[j])) { found = true } + } + if (!found) { + log(logSpace, 'This one groups: ' + cardGroups) + log(logSpace, 'Other one groups: ' + otherGroups) + log(logSpace, 'Cant delete this one because it has a group, ' + cardGroups[j] + ', which the other does not.') + stats.nameOnlyDuplicates.push(card) + return resolve(false) } - log('Deleting ... ' + resource) - kb.fetcher.delete(resource) - .then(function () { - log('Deleted ok: ' + resource) - deleteNextFile() - }) - .catch(function (e) { - var err = '*** ERROR deleting ' + resource + ': ' + e - log(err) - if (confirm('Patch out index file for card ' + card.dir() + ' EVEN THOUGH card DELETE errors?')) { - removeFromMainIndex() - } else { - reject(err) - } - }) } - deleteNextFile() - }) // Promise - } // erase one - */ - // Check actual records to see which are exact matches - slow - stats.nameDupLog = kb.sym(book.dir().uri + 'dedup-nameDupLog.ttl') - stats.exactDupLog = kb.sym(book.dir().uri + 'dedup-exactDupLog.ttl') - /* - function checkOne (card) { - return new Promise(function (resolve, reject) { - var name = kb.anyValue(card, ns.vcard('fn')) - var other = stats.definitive[name] - kb.fetcher.load([card, other]).then(function (xhrs) { - var exclude = {} + debug.log('Group check done -- exact duplicate: ' + card) + stats.exactDuplicates.push(card) + resolve(true) + }).catch(function (e) { + log(logSpace, 'Cant load a card! ' + [card, other] + ': ' + e) + stats.nameOnlyDuplicates.push(card) + resolve(false) + // if (confirm('Patch out index file for card ' + card.dir() + ' EVEN THOUGH card READ errors?')){ + // removeFromMainIndex() + // } + }) + }) + } // checkOne +*/ + stats.nameOnlyErrors = [] + stats.nameLessZeroData = [] + stats.nameLessIndex = [] + stats.namelessUniques = [] + stats.nameOnlyDuplicatesGroupDiff = [] + + function checkOneNameless (card) { + return new Promise(function (resolve) { + kb.fetcher + .load(card) + .then(function (_xhr) { + log(logSpace, ' Nameless check ' + card) + const exclude = {} exclude[ns.vcard('hasUID').uri] = true exclude[ns.dc('created').uri] = true exclude[ns.dc('modified').uri] = true function filtered (x) { - return kb.statementsMatching(null, null, null, x.doc()).filter(function (st) { - return !exclude[st.predicate.uri] - }) + return kb + .statementsMatching(null, null, null, x.doc()) + .filter(function (st) { + return !exclude[st.predicate.uri] + }) } - var desc = filtered(card) - var desc2 = filtered(other) + + const desc = filtered(card) // var desc = connectedStatements(card, card.doc(), exclude) // var desc2 = connectedStatements(other, other.doc(), exclude) - if (desc.length !== desc2.length) { - log('CARDS to NOT match lengths ') - stats.nameOnlyDuplicates.push(card) - return resolve(false) - } if (!desc.length) { - log('@@@@@@ Zero length ') - stats.nameOnlyDuplicates.push(card) + log(logSpace, ' Zero length ' + card) + stats.nameLessZeroData.push(card) return resolve(false) } - // //////// Compare the two + // Compare the two // Cheat: serialize and compare // var cardText = $rdf.serialize(card.doc(), kb, card.doc().uri, 'text/turtle') // var otherText = $rdf.serialize(other.doc(), kb, other.doc().uri, 'text/turtle') - var cardText = (new $rdf.Serializer(kb)).setBase(card.doc().uri).statementsToN3(desc) - var otherText = (new $rdf.Serializer(kb)).setBase(other.doc().uri).statementsToN3(desc2) - // - // log('Name: ' + name + ', statements: ' + desc.length) - // log('___________________________________________') - // log('KEEPING: ' + other.doc() + '\n' + cardText) - // log('___________________________________________') - // log('DELETING: '+ card.doc() + '\n' + otherText) - // log('___________________________________________') - // - if (cardText !== otherText) { - log('Texts differ') - stats.nameOnlyDuplicates.push(card) - return resolve(false) - } - var cardGroups = kb.each(null, ns.vcard('hasMember'), card) - var otherGroups = kb.each(null, ns.vcard('hasMember'), other) - for (var j = 0; j < cardGroups.length; j++) { - var found = false - for (var k = 0; k < otherGroups.length; k++) { - if (otherGroups[k].sameTerm(cardGroups[j])) { found = true } - } - if (!found) { - log('This one groups: ' + cardGroups) - log('Other one groups: ' + otherGroups) - log('Cant delete this one because it has a group, ' + cardGroups[j] + ', which the other does not.') - stats.nameOnlyDuplicates.push(card) - return resolve(false) + const cardText = new $rdf.Serializer(kb) + .setBase(card.doc().uri) + .statementsToN3(desc) + const other = stats.nameLessIndex[cardText] + if (other) { + log(logSpace, ' Matches with ' + other) + // alain not sure it works we may need to concat with 'sameAs' group.doc (.map(st => st.why)) + const cardGroups = kb.each(null, ns.vcard('hasMember'), card) + const otherGroups = kb.each(null, ns.vcard('hasMember'), other) + for (let j = 0; j < cardGroups.length; j++) { + let found = false + for (let k = 0; k < otherGroups.length; k++) { + if (otherGroups[k].sameTerm(cardGroups[j])) found = true + } + if (!found) { + log(logSpace, 'This one groups: ' + cardGroups) + log(logSpace, 'Other one groups: ' + otherGroups) + log( + logSpace, + 'Cant skip this one because it has a group, ' + + cardGroups[j] + + ', which the other does not.' + ) + stats.nameOnlyDuplicatesGroupDiff.push(card) + return resolve(false) + } } + debug.log('Group check done -- exact duplicate: ' + card) + } else { + log(logSpace, 'First nameless like: ' + card.doc()) + log(logSpace, '___________________________________________') + log(logSpace, cardText) + log(logSpace, '___________________________________________') + stats.nameLessIndex[cardText] = card + stats.namelessUniques.push(card) } - console.log('Group check done -- exact duplicate: ' + card) - stats.exactDuplicates.push(card) resolve(true) - }).catch(function (e) { - log('Cant load a card! ' + [card, other] + ': ' + e) - stats.nameOnlyDuplicates.push(card) + }) + .catch(function (e) { + log(logSpace, 'Cant load a nameless card!: ' + e) + stats.nameOnlyErrors.push(card) resolve(false) - // if (confirm('Patch out index file for card ' + card.dir() + ' EVEN THOUGH card READ errors?')){ - // removeFromMainIndex() - // } }) - }) - } // checkOne - */ - stats.nameOnlyErrors = [] - stats.nameLessZeroData = [] - stats.nameLessIndex = [] - stats.namelessUniques = [] - stats.nameOnlyDuplicatesGroupDiff = [] - - function checkOneNameless (card) { - return new Promise(function (resolve) { - kb.fetcher - .load(card) - .then(function (_xhr) { - log(' Nameless check ' + card) - const exclude = {} - exclude[ns.vcard('hasUID').uri] = true - exclude[ns.dc('created').uri] = true - exclude[ns.dc('modified').uri] = true - function filtered (x) { - return kb - .statementsMatching(null, null, null, x.doc()) - .filter(function (st) { - return !exclude[st.predicate.uri] - }) - } - - const desc = filtered(card) - // var desc = connectedStatements(card, card.doc(), exclude) - // var desc2 = connectedStatements(other, other.doc(), exclude) - if (!desc.length) { - log(' Zero length ' + card) - stats.nameLessZeroData.push(card) - return resolve(false) - } - // Compare the two - // Cheat: serialize and compare - // var cardText = $rdf.serialize(card.doc(), kb, card.doc().uri, 'text/turtle') - // var otherText = $rdf.serialize(other.doc(), kb, other.doc().uri, 'text/turtle') - const cardText = new $rdf.Serializer(kb) - .setBase(card.doc().uri) - .statementsToN3(desc) - const other = stats.nameLessIndex[cardText] - if (other) { - log(' Matches with ' + other) - // alain not sure it works we may need to concat with 'sameAs' group.doc (.map(st => st.why)) - const cardGroups = kb.each(null, ns.vcard('hasMember'), card) - const otherGroups = kb.each(null, ns.vcard('hasMember'), other) - for (let j = 0; j < cardGroups.length; j++) { - let found = false - for (let k = 0; k < otherGroups.length; k++) { - if (otherGroups[k].sameTerm(cardGroups[j])) found = true - } - if (!found) { - log('This one groups: ' + cardGroups) - log('Other one groups: ' + otherGroups) - log( - 'Cant skip this one because it has a group, ' + - cardGroups[j] + - ', which the other does not.' - ) - stats.nameOnlyDuplicatesGroupDiff.push(card) - return resolve(false) - } - } - console.log('Group check done -- exact duplicate: ' + card) - } else { - log('First nameless like: ' + card.doc()) - log('___________________________________________') - log(cardText) - log('___________________________________________') - stats.nameLessIndex[cardText] = card - stats.namelessUniques.push(card) - } - resolve(true) - }) - .catch(function (e) { - log('Cant load a nameless card!: ' + e) - stats.nameOnlyErrors.push(card) - resolve(false) - }) - }) - } // checkOneNameless - - function checkAllNameless () { - stats.namelessToCheck = - stats.namelessToCheck || stats.nameless.slice() - log('Nameless check left: ' + stats.namelessToCheck.length) - return new Promise(function (resolve) { - const x = stats.namelessToCheck.shift() - if (!x) { - log('namelessUniques: ' + stats.namelessUniques.length) - log('namelessUniques: ' + stats.namelessUniques) - if (stats.namelessUniques.length > 0 && - confirm( - 'Add all ' + - stats.namelessUniques.length + - ' nameless cards to the rescued set?' - ) - ) { + }) + } // checkOneNameless + + function checkAllNameless () { + stats.namelessToCheck = + stats.namelessToCheck || stats.nameless.slice() + log(logSpace, 'Nameless check left: ' + stats.namelessToCheck.length) + return new Promise(function (resolve) { + const x = stats.namelessToCheck.shift() + if (!x) { + log(logSpace, 'namelessUniques: ' + stats.namelessUniques.length) + log(logSpace, 'namelessUniques: ' + stats.namelessUniques) + if (stats.namelessUniques.length > 0) { + const msg = dom.createElement('p') + msg.textContent = 'Add all ' + stats.namelessUniques.length + ' nameless contacts to the rescued set?' + divStatistics.appendChild(msg) + const confirmButton = UI.widgets.continueButton(dom, function () { stats.uniques = stats.uniques.concat(stats.namelessUniques) for (let k = 0; k < stats.namelessUniques.length; k++) { stats.uniqueSet[stats.namelessUniques[k].uri] = true } - } + msg.remove() + confirmButton.remove() + resolve(true) + }) + divStatistics.appendChild(confirmButton) + } else { return resolve(true) } - checkOneNameless(x).then(function (exact) { - log(' Nameless check returns ' + exact) - checkAllNameless() // loop - }) + return + } + checkOneNameless(x).then(function (exact) { + log(logSpace, ' Nameless check returns ' + exact) + checkAllNameless() // loop }) - } + }) + } - function checkGroupMembers () { - return new Promise(function (resolve) { - // var inUniques = 0 - log('Groups loaded') - for (let i = 0; i < stats.uniques.length; i++) { - stats.uniquesSet[stats.uniques[i].uri] = true - } - stats.groupMembers = [] - kb.each(null, ns.vcard('hasMember')) - .forEach(group => { stats.groupMembers = stats.groupMembers.concat(groupMembers(kb, group)) }) - log(' Naive group members ' + stats.groupMembers.length) - stats.groupMemberSet = [] - for (let j = 0; j < stats.groupMembers.length; j++) { - stats.groupMemberSet[stats.groupMembers[j].uri] = - stats.groupMembers[j] - } - stats.groupMembers2 = [] - for (const g in stats.groupMemberSet) { - stats.groupMembers2.push(stats.groupMemberSet[g]) - } - log(' Compact group members ' + stats.groupMembers2.length) - - if ( - $rdf.keepThisCodeForLaterButDisableFerossConstantConditionPolice - ) { - // Don't inspect as seems groups membership is complete - for (let i = 0; i < stats.groupMembers.length; i++) { - const card = stats.groupMembers[i] - if (stats.uniquesSet[card.uri]) { - // inUniques += 1 + function checkGroupMembers () { + return new Promise(function (resolve) { + // var inUniques = 0 + log(logSpace, 'Groups loaded') + for (let i = 0; i < stats.uniques.length; i++) { + stats.uniquesSet[stats.uniques[i].uri] = true + } + stats.groupMembers = [] + kb.each(null, ns.vcard('hasMember')) + .forEach(group => { stats.groupMembers = stats.groupMembers.concat(groupMembers(kb, group)) }) + log(logSpace, ' Naive group members ' + stats.groupMembers.length) + stats.groupMemberSet = [] + for (let j = 0; j < stats.groupMembers.length; j++) { + stats.groupMemberSet[stats.groupMembers[j].uri] = + stats.groupMembers[j] + } + stats.groupMembers2 = [] + for (const g in stats.groupMemberSet) { + stats.groupMembers2.push(stats.groupMemberSet[g]) + } + log(logSpace, ' Compact group members ' + stats.groupMembers2.length) + + if ( + $rdf.keepThisCodeForLaterButDisableFerossConstantConditionPolice + ) { + // Don't inspect as seems groups membership is complete + for (let i = 0; i < stats.groupMembers.length; i++) { + const card = stats.groupMembers[i] + if (stats.uniquesSet[card.uri]) { + // inUniques += 1 + } else { + log(logSpace, ' Not in uniques: ' + card) + stats.groupProblems.push(card) + if (stats.duplicateSet[card.uri]) { + log(logSpace, ' ** IN duplicates alas:' + card) } else { - log(' Not in uniques: ' + card) - stats.groupProblems.push(card) - if (stats.duplicateSet[card.uri]) { - log(' ** IN duplicates alas:' + card) - } else { - log(' **** WTF?') - } - } - } - log('Problem cards: ' + stats.groupProblems.length) - } // if - resolve(true) - }) - } // checkGroupMembers - - function scanForDuplicates () { - return new Promise(function (resolve) { - stats.cards = kb.each(undefined, VCARD('inAddressBook'), stats.book) - log('' + stats.cards.length + ' total cards') - - let c, card, name - for (c = 0; c < stats.cards.length; c++) { - card = stats.cards[c] - name = kb.anyValue(card, ns.vcard('fn')) - if (!name) { - stats.nameless.push(card) - continue - } - if (stats.definitive[name] === card) { - // pass - } else if (stats.definitive[name]) { - const n = stats.duplicates.length - if (n < 100 || (n < 1000 && n % 10 === 0) || n % 100 === 0) { - // log('' + n + ') Possible duplicate ' + card + ' of: ' + definitive[name]) + log(logSpace, ' **** WTF?') } - stats.duplicates.push(card) - } else { - stats.definitive[name] = card } } - - stats.duplicateSet = [] - for (let i = 0; i < stats.duplicates.length; i++) { - stats.duplicateSet[stats.duplicates[i].uri] = stats.duplicates[i] - } - stats.namelessSet = [] - for (let i = 0; i < stats.nameless.length; i++) { - stats.namelessSet[stats.nameless[i].uri] = stats.nameless[i] + log(logSpace, 'Problem contacts: ' + stats.groupProblems.length) + } // if + resolve(true) + }) + } // checkGroupMembers + + function scanForDuplicates () { + return new Promise(function (resolve) { + stats.cards = kb.each(undefined, VCARD('inAddressBook'), stats.book) + log(logSpace, '' + stats.cards.length + ' total contacts') + + let c, card, name + for (c = 0; c < stats.cards.length; c++) { + card = stats.cards[c] + name = kb.anyValue(card, ns.vcard('fn')) + if (!name) { + stats.nameless.push(card) + continue } - stats.uniques = [] - stats.uniqueSet = [] - for (let i = 0; i < stats.cards.length; i++) { - const uri = stats.cards[i].uri - if (!stats.duplicateSet[uri] && !stats.namelessSet[uri]) { - stats.uniques.push(stats.cards[i]) - stats.uniqueSet[uri] = stats.cards[i] + if (stats.definitive[name] === card) { + // pass + } else if (stats.definitive[name]) { + const n = stats.duplicates.length + if (n < 100 || (n < 1000 && n % 10 === 0) || n % 100 === 0) { + // log('' + n + ') Possible duplicate ' + card + ' of: ' + definitive[name]) } + stats.duplicates.push(card) + } else { + stats.definitive[name] = card } - log('Uniques: ' + stats.uniques.length) - - log('' + stats.nameless.length + ' nameless cards.') - log( - '' + - stats.duplicates.length + - ' name-duplicate cards, leaving ' + - (stats.cards.length - stats.duplicates.length) - ) - resolve(true) - }) - } + } - // Save a new clean version - function saveCleanPeople () { - let cleanPeople - - return Promise.resolve() - .then(() => { - cleanPeople = kb.sym(stats.book.dir().uri + 'clean-people.ttl') - let sts = [] - for (let i = 0; i < stats.uniques.length; i++) { - sts = sts.concat( - kb.connectedStatements(stats.uniques[i], stats.nameEmailIndex) - ) - } - const sz = new $rdf.Serializer(kb).setBase(stats.nameEmailIndex.uri) - log('Serializing index of uniques...') - const data = sz.statementsToN3(sts) + stats.duplicateSet = [] + for (let i = 0; i < stats.duplicates.length; i++) { + stats.duplicateSet[stats.duplicates[i].uri] = stats.duplicates[i] + } + stats.namelessSet = [] + for (let i = 0; i < stats.nameless.length; i++) { + stats.namelessSet[stats.nameless[i].uri] = stats.nameless[i] + } + stats.uniques = [] + stats.uniqueSet = [] + for (let i = 0; i < stats.cards.length; i++) { + const uri = stats.cards[i].uri + if (!stats.duplicateSet[uri] && !stats.namelessSet[uri]) { + stats.uniques.push(stats.cards[i]) + stats.uniqueSet[uri] = stats.cards[i] + } + } + log(logSpace, 'Uniques: ' + stats.uniques.length) + + log(logSpace, '' + stats.nameless.length + ' nameless contacts.') + log( + logSpace, + '' + + stats.duplicates.length + + ' name-duplicate contacts, leaving ' + + (stats.cards.length - stats.duplicates.length) + ) + resolve(true) + }) + } - return kb.fetcher.webOperation('PUT', cleanPeople, { - data, - contentType: 'text/turtle' - }) - }) - .then(function () { - log('Done uniques log ' + cleanPeople) - return true - }) - .catch(function (e) { - log('Error saving uniques: ' + e) - }) - } + // Save a new clean version + function saveCleanPeople () { + let cleanPeople - function saveCleanGroup (g) { - let cleanGroup - - return Promise.resolve() - .then(() => { - const s = g.uri.replace('/Group/', '/NewGroup/') - cleanGroup = kb.sym(s) - let sts = [] - for (let i = 0; i < stats.uniques.length; i++) { - sts = sts.concat( - kb.connectedStatements(stats.uniques[i], g.doc()) - ) - } - const sz = new $rdf.Serializer(kb).setBase(g.uri) - log(' Regenerating group of uniques...' + cleanGroup) - const data = sz.statementsToN3(sts) + return Promise.resolve() + .then(() => { + cleanPeople = kb.sym(stats.book.dir().uri + 'clean-people.ttl') + let sts = [] + for (let i = 0; i < stats.uniques.length; i++) { + sts = sts.concat( + kb.connectedStatements(stats.uniques[i], stats.nameEmailIndex) + ) + } + const sz = new $rdf.Serializer(kb).setBase(stats.nameEmailIndex.uri) + log(logSpace, 'Serializing index of uniques...') + const data = sz.statementsToN3(sts) - return kb.fetcher.webOperation('PUT', cleanGroup, { - data, - contentType: 'text/turtle' - }) + return kb.fetcher.webOperation('PUT', cleanPeople, { + data, + contentType: 'text/turtle' }) - .then(() => { - log(' Done uniques group ' + cleanGroup) - return true - }) - .catch(e => { - log('Error saving : ' + e) - }) - } + }) + .then(function () { + log(logSpace, 'Done uniques log ' + cleanPeople) + return true + }) + .catch(function (e) { + log(logSpace, 'Error saving uniques: ' + e) + }) + } - function saveAllGroups () { - log('Saving ALL GROUPS') - return Promise.all(stats.groupObjects.map(saveCleanGroup)) - } + function saveCleanGroup (g) { + let cleanGroup - const getAndSortGroups = function () { - let groups = [] - if (stats.book) { - const books = [stats.book] - books.forEach(function (book) { - const gs = book ? kb.each(book, ns.vcard('includesGroup')) : [] - const gs2 = gs.map(function (g) { - return [book, kb.any(g, ns.vcard('fn')), g] - }) - groups = groups.concat(gs2) - }) - groups.sort() - } - return groups - } - const groups = getAndSortGroups() // Needed? - - stats.groupObjects = groups.map(gstr => gstr[2]) - log('Loading ' + stats.groupObjects.length + ' groups... ') - kb.fetcher - .load(stats.groupObjects) - .then(scanForDuplicates) - .then(checkGroupMembers) - .then(checkAllNameless) + return Promise.resolve() .then(() => { - return new Promise(function (resolve, reject) { - if (confirm('Write new clean versions?')) { - resolve(true) - } else { - reject(new Error('User cancelled writing clean versions')) - } + const s = g.uri.replace('/Group/', '/NewGroup/') + cleanGroup = kb.sym(s) + let sts = [] + for (let i = 0; i < stats.uniques.length; i++) { + sts = sts.concat( + kb.connectedStatements(stats.uniques[i], g.doc()) + ) + } + const sz = new $rdf.Serializer(kb).setBase(g.uri) + log(logSpace, ' Regenerating group of uniques...' + cleanGroup) + const data = sz.statementsToN3(sts) + + return kb.fetcher.webOperation('PUT', cleanGroup, { + data, + contentType: 'text/turtle' }) }) - .then(saveCleanPeople) - .then(saveAllGroups) - .then(function () { - log('Done!') + .then(() => { + log(logSpace, ' Done uniques group ' + cleanGroup) + return true + }) + .catch(e => { + log(logSpace, 'Error saving : ' + e) }) } - ) - }) - async function fixGroupless (book) { - const groupless = await getGroupless(book) - if (groupless.length === 0) { - log('No groupless cards found.') - return - } - const groupOfUngrouped = await saveNewGroup(book, 'ZSortThese') - if (confirm(`Add the ${groupless.length} cards without groups to a ZSortThese group?`)) { - for (const person of groupless) { - log(' adding ' + person) - await addPersonToGroup(person, groupOfUngrouped) + function saveAllGroups () { + log(logSpace, 'Saving ALL GROUPS') + return Promise.all(stats.groupObjects.map(saveCleanGroup)) + } + + const getAndSortGroups = function () { + let groups = [] + if (stats.book) { + const books = [stats.book] + books.forEach(function (book) { + const gs = book ? kb.each(book, ns.vcard('includesGroup')) : [] + const gs2 = gs.map(function (g) { + return [book, kb.any(g, ns.vcard('fn')), g] + }) + groups = groups.concat(gs2) + }) + groups.sort() + } + return groups } + const groups = getAndSortGroups() // Needed? + + stats.groupObjects = groups.map(gstr => gstr[2]) + log(logSpace, 'Loading ' + stats.groupObjects.length + ' groups... ') + kb.fetcher + .load(stats.groupObjects) + .then(scanForDuplicates) + .then(checkGroupMembers) + .then(checkAllNameless) + .then(saveCleanPeople) + .then(saveAllGroups) + .then(function () { + log(logSpace, 'Done!') + }) + } + ) + }) + + const checkGroupless = buttonsContainer.appendChild(dom.createElement('button')) + checkGroupless.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + checkGroupless.textContent = 'Find contacts with no group' + checkGroupless.addEventListener('click', function (_event) { + setActiveButton(checkGroupless) + logSpace.textContent = '' + log(logSpace, 'Loading groups...') + selectAllGroups(selectedGroups, groupsMainTable, async function (ok, message) { + if (!ok) { + log(logSpace, 'Load all groups: failed: ' + message) + return } - log('People moved to group.') - } - async function getGroupless (book) { - const groupIndex = kb.any(book, ns.vcard('groupIndex')) const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) try { - await kb.fetcher.load([nameEmailIndex, groupIndex]) - const groups = kb.each(book, ns.vcard('includesGroup')) - await kb.fetcher.load(groups) + await kb.fetcher.load(nameEmailIndex) } catch (e) { - complain('Error loading stuff:' + e) + complain(e) } - - const reverseIndex = {} - const groupless = [] - let groups = kb.each(book, VCARD('includesGroup')) - const strings = new Set(groups.map(group => group.uri)) // remove dups - groups = [...strings].map(uri => kb.sym(uri)) - log('' + groups.length + ' total groups. ') - - for (let i = 0; i < groups.length; i++) { - const g = groups[i] - const a = groupMembers(kb, g) - - log(UI.utils.label(g) + ': ' + a.length + ' members') - for (let j = 0; j < a.length; j++) { - kb.allAliases(a[j]).forEach(function (y) { - reverseIndex[y.uri] = g - }) - } + log(logSpace, 'Loaded groups and name index.') + getGroupless(book) + log(logSpace, 'Groupless list finished..') + }) // select all groups then + }) + + const fixGrouplessButton = buttonsContainer.appendChild(dom.createElement('button')) + fixGrouplessButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + fixGrouplessButton.textContent = 'Put all individuals with no group in a new group' + fixGrouplessButton.addEventListener('click', _event => { + setActiveButton(fixGrouplessButton) + logSpace.textContent = '' + fixGroupless(book) + }) + + const fixToOldDataModelButton = buttonsContainer.appendChild(dom.createElement('button')) + fixToOldDataModelButton.classList.add('actionButton', 'btn-secondary', 'action-button-focus') + fixToOldDataModelButton.textContent = 'Revert groups to old data model' + fixToOldDataModelButton.addEventListener('click', _event => { + setActiveButton(fixToOldDataModelButton) + logSpace.textContent = '' + fixToOldDataModel(book) + }) + return pane +} + +async function checkAcces (_event) { + function doCard (card) { + UI.acl.fixIndividualCardACL(card, (msg) => log(logSpace, msg), function (ok, message) { + if (ok) { + log(logSpace, 'Success for ' + UI.utils.label(card)) + } else { + log(logSpace, 'Failure for ' + UI.utils.label(card) + ': ' + message) } + }) + } + const gg = [] + for (const g in selectedGroups) { + gg.push(g) + } - const cards = kb.each(undefined, VCARD('inAddressBook'), book) - log('' + cards.length + ' total cards') - for (let c = 0; c < cards.length; c++) { - if (!reverseIndex[cards[c].uri]) { - groupless.push(cards[c]) - log(' groupless ' + UI.utils.label(cards[c])) - } - } - log('' + groupless.length + ' groupless cards.') - return groupless + for (let i = 0; i < gg.length; i++) { + const g = kb.sym(gg[i]) + const a = groupMembers(kb, g) + log(logSpace, UI.utils.label(g) + ': ' + a.length + ' members') + for (let j = 0; j < a.length; j++) { + const card = a[j] + log(logSpace, UI.utils.label(card)) + doCard(card) } + } +} + +function log (logSpace, message) { + debug.log(message) + logSpace.textContent += message + '\n' +} + +function stats (logSpace) { + const totalContacts = kb.each(undefined, VCARD('inAddressBook'), book).length + log(logSpace, '' + totalContacts + ' contacts loaded. ') + let groups = kb.each(book, VCARD('includesGroup')) + const strings = new Set(groups.map(group => normalizeGroupUri(group.uri))) // remove dups with normalized URIs + groups = [...strings].map(uri => kb.sym(uri)) + log(logSpace, '' + groups.length + ' total groups. ') + const gg = [] + for (const g in selectedGroups) { + gg.push(g) + } + log(logSpace, '' + gg.length + ' selected groups. ') +} + +async function loadIndexHandler (loadIndexButton, logSpace) { + loadIndexButton.classList.add('toolsButton--loading') + loadIndexButton.classList.remove('toolsButton--error', 'toolsButton--success') + const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) + try { + await kb.fetcher.load(nameEmailIndex) + } catch (e) { + loadIndexButton.classList.remove('toolsButton--loading') + loadIndexButton.classList.add('toolsButton--error') + log(logSpace, 'Error: People index has NOT been loaded' + e + '\n') + } + loadIndexButton.classList.remove('toolsButton--loading') + loadIndexButton.classList.add('toolsButton--success') + log(logSpace, ' People index has been loaded\n') +} // loadIndexHandler + +async function fixGroupless (book) { + const groupless = await getGroupless(book) + if (groupless.length === 0) { + log(logSpace, 'No groupless contacts found.') + return + } + const groupOfUngrouped = await saveNewGroup(book, 'No group') + const dom = logSpace.ownerDocument + return new Promise(function (resolve) { + const msg = dom.createElement('p') + msg.textContent = `Add the ${groupless.length} contacts without groups to a 'No group' group?` + logSpace.appendChild(msg) + const confirmButton = UI.widgets.continueButton(dom, async function () { + msg.remove() + confirmButton.remove() + for (const person of groupless) { + log(logSpace, ' adding ' + UI.utils.label(person)) + await addPersonToGroup(person, groupOfUngrouped) + } + log(logSpace, 'People moved to group.') + if (refreshGroupsFn) refreshGroupsFn() + resolve() + }) + logSpace.appendChild(confirmButton) + }) +} + +async function getGroupless (book) { + const groupIndex = kb.any(book, ns.vcard('groupIndex')) + const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) + try { + await kb.fetcher.load([nameEmailIndex, groupIndex]) + const groups = kb.each(book, ns.vcard('includesGroup')) + await kb.fetcher.load(groups) + } catch (e) { + complain('Error loading stuff:' + e) + } - const checkGroupless = pane.appendChild(dom.createElement('button')) - checkGroupless.style.cssText = buttonStyle - checkGroupless.textContent = 'Find individuals with no group' - checkGroupless.addEventListener('click', function (_event) { - log('Loading groups...') - selectAllGroups(selectedGroups, groupsMainTable, async function (ok, message) { - if (!ok) { - log('Load all groups: failed: ' + message) - return - } + const reverseIndex = {} + const groupless = [] + let groups = kb.each(book, VCARD('includesGroup')) + const strings = new Set(groups.map(group => normalizeGroupUri(group.uri))) // remove dups with normalized URIs + groups = [...strings].map(uri => kb.sym(uri)) + log(logSpace, '' + groups.length + ' total groups. ') + + for (let i = 0; i < groups.length; i++) { + const g = groups[i] + const a = groupMembers(kb, g) + + log(logSpace, UI.utils.label(g) + ': ' + a.length + ' members') + for (let j = 0; j < a.length; j++) { + kb.allAliases(a[j]).forEach(function (y) { + reverseIndex[y.uri] = g + }) + } + } - const nameEmailIndex = kb.any(book, ns.vcard('nameEmailIndex')) - try { - await kb.fetcher.load(nameEmailIndex) - } catch (e) { - complain(e) + const cards = kb.each(undefined, VCARD('inAddressBook'), book) + log(logSpace, '' + cards.length + ' total contatcs') + for (let c = 0; c < cards.length; c++) { + if (!reverseIndex[cards[c].uri]) { + groupless.push(cards[c]) + log(logSpace, ' groupless ' + UI.utils.label(cards[c])) + } + } + log(logSpace, '' + groupless.length + ' groupless contacts.') + return groupless +} + +async function fixToOldDataModel (book) { + async function updateToOldDataModel (groups) { + let ds = [] + let ins = [] + groups.forEach(group => { + let vcardOrWebids = kb.statementsMatching(null, ns.owl('sameAs'), null, group.doc()).map(st => st.subject) + const strings = new Set(vcardOrWebids.map(contact => contact.uri)) // remove dups + vcardOrWebids = [...strings].map(uri => kb.sym(uri)) + vcardOrWebids.forEach(item => { + if (!kb.each(item, ns.vcard('fn'), null, group.doc()).length) { + // delete item this is a new data model, item is a webid not a card. + ds = ds.concat(kb + .statementsMatching(item, ns.owl('sameAs'), null, group.doc()) + .concat(kb.statementsMatching(undefined, undefined, item, group.doc()))) + // add webid card to group + const cards = kb.each(item, ns.owl('sameAs'), null, group.doc()) + cards.forEach(card => { + ins = ins.concat($rdf.st(card, ns.owl('sameAs'), item, group.doc())) + .concat($rdf.st(group, ns.vcard('hasMember'), card, group.doc())) + }) } - log('Loaded groups and name index.') - getGroupless(book) - log('Groupless list finished..') - }) // select all groups then + }) }) - - const fixGrouplessButton = pane.appendChild(dom.createElement('button')) - fixGrouplessButton.style.cssText = buttonStyle - fixGrouplessButton.textContent = 'Put all individuals with no group in a new group' - fixGrouplessButton.addEventListener('click', _event => fixGroupless(book)) - - async function fixToOldDataModel (book) { - async function updateToOldDataModel (groups) { - let ds = [] - let ins = [] - groups.forEach(group => { - let vcardOrWebids = kb.statementsMatching(null, ns.owl('sameAs'), null, group.doc()).map(st => st.subject) - const strings = new Set(vcardOrWebids.map(contact => contact.uri)) // remove dups - vcardOrWebids = [...strings].map(uri => kb.sym(uri)) - vcardOrWebids.forEach(item => { - if (!kb.each(item, ns.vcard('fn'), null, group.doc()).length) { - // delete item this is a new data model, item is a webid not a card. - ds = ds.concat(kb - .statementsMatching(item, ns.owl('sameAs'), null, group.doc()) - .concat(kb.statementsMatching(undefined, undefined, item, group.doc()))) - // add webid card to group - const cards = kb.each(item, ns.owl('sameAs'), null, group.doc()) - cards.forEach(card => { - ins = ins.concat($rdf.st(card, ns.owl('sameAs'), item, group.doc())) - .concat($rdf.st(group, ns.vcard('hasMember'), card, group.doc())) - }) - } - }) - }) - if (ds.length && confirm('Groups can be updated to old data model ?')) { + if (ds.length) { + const dom = logSpace.ownerDocument + return new Promise(function (resolve) { + const msg = dom.createElement('p') + msg.textContent = 'Groups can be updated to old data model?' + logSpace.appendChild(msg) + const confirmButton = UI.widgets.continueButton(dom, async function () { + msg.remove() + confirmButton.remove() await kb.updater.updateMany(ds, ins) - alert('Update done') - } else { if (!ds.length) alert('Nothing to update.\nAll Groups already use the old data model.') } - } - let groups = kb.each(book, VCARD('includesGroup')) - const strings = new Set(groups.map(group => group.uri)) // remove dups - groups = [...strings].map(uri => kb.sym(uri)) - updateToOldDataModel(groups) + log(logSpace, 'Update done') + resolve() + }) + logSpace.appendChild(confirmButton) + }) + } else { + log(logSpace, 'Nothing to update.\nAll groups already use the old data model.') } - - const fixToOldDataModelButton = pane.appendChild(dom.createElement('button')) - fixToOldDataModelButton.style.cssText = buttonStyle - fixToOldDataModelButton.textContent = 'Revert groups to old data model' - fixToOldDataModelButton.addEventListener('click', _event => fixToOldDataModel(book)) - } // main - main() - return pane -} // toolsPane - -// ends + } + let groups = kb.each(book, VCARD('includesGroup')) + const strings = new Set(groups.map(group => normalizeGroupUri(group.uri))) // remove dups with normalized URIs + groups = [...strings].map(uri => kb.sym(uri)) + updateToOldDataModel(groups) +} diff --git a/src/webidControl.js b/src/webidControl.js index f28d50e..a795ae4 100644 --- a/src/webidControl.js +++ b/src/webidControl.js @@ -3,25 +3,22 @@ import * as UI from 'solid-ui' import { store } from 'solid-logic' import { updateMany } from './contactLogic' -// import { renderAutoComplete } from './lib/autocompletePicker' // dbpediaParameters -import { renderAutocompleteControl } from './autocompleteBar' -// import { wikidataParameters, loadPublicDataThing, wikidataClasses } from './lib/publicData' // dbpediaParameters import * as $rdf from 'rdflib' +import './styles/webidControl.css' +import * as debug from './debug' const ns = UI.ns const widgets = UI.widgets const utils = UI.utils const kb = store -const style = UI.style const wikidataClasses = widgets.publicData.wikidataClasses // @@ move to solid-logic const wikidataParameters = widgets.publicData.wikidataParameters // @@ move to solid-logic -const WEBID_NOUN = 'Solid ID' -const PUBLICID_NOUN = 'In public data' +const WEBID_NOUN = 'WebID' +const PUBLICID_NOUN = 'WikiData link' const DOWN_ARROW = UI.icons.iconBase + 'noun_1369241.svg' const UP_ARROW = UI.icons.iconBase + 'noun_1369237.svg' -const webidPanelBackgroundColor = '#ffe6ff' /// ///////////////////////// Logic @@ -45,7 +42,7 @@ export async function addWebIDToContacts (person, webid, urlType, kb) { } // create a person's webID - console.log(`Adding to ${person} a ${WEBID_NOUN}: ${webid}.`) + debug.log(`Adding to ${person} a ${WEBID_NOUN}: ${webid}.`) const vcardURLThing = kb.bnode() const insertables = [ $rdf.st(person, ns.vcard('url'), vcardURLThing, person.doc()), @@ -68,7 +65,7 @@ export async function addWebIDToContacts (person, webid, urlType, kb) { } export async function removeWebIDFromContacts (person, webid, urlType, kb) { - console.log(`Removing from ${person} their ${WEBID_NOUN}: ${webid}.`) + debug.log(`Removing from ${person} their ${WEBID_NOUN}: ${webid}.`) // remove webID from card const existing = kb.each(person, ns.vcard('url'), null, person.doc()) @@ -101,7 +98,7 @@ export async function removeWebIDFromContacts (person, webid, urlType, kb) { // Trace things the same as this - other IDs for same thing // returns as array of node -export function getSameAs (kb, thing, doc) { // Should this recurse? +function getSameAs (kb, thing, doc) { // Should this recurse? const found = new Set() const agenda = new Set([thing.uri]) @@ -114,13 +111,13 @@ export function getSameAs (kb, thing, doc) { // Should this recurse? kb.each(node, ns.owl('sameAs'), null, doc) .concat(kb.each(null, ns.owl('sameAs'), node, doc)) .forEach(next => { - console.log(' OWL sameAs found ' + next) + debug.log(' OWL sameAs found ' + next) agenda.add(next.uri) }) kb.each(node, ns.schema('sameAs'), null, doc) .concat(kb.each(null, ns.schema('sameAs'), node, doc)) .forEach(next => { - console.log(' Schema sameAs found ' + next) + debug.log(' Schema sameAs found ' + next) agenda.add(next.uri) }) } @@ -157,18 +154,13 @@ export function isOrganization (agent) { export function renderNamedPane (dom, subject, paneName, dataBrowserContext) { const p = dataBrowserContext.session.paneRegistry.byName(paneName) const d = p.render(subject, dataBrowserContext) // @@@ change some bits of context! - d.setAttribute( - 'style', - 'border: 0.1em solid #444; border-radius: 0.5em' - ) + d.classList.add('namedPane') return d } export async function renderWebIdControl (person, dataBrowserContext) { const options = { - longPrompt: `If you know someone's ${WEBID_NOUN}, you can do more stuff with them. - To record their ${WEBID_NOUN}, drag it onto the plus, or click the plus - to enter it by hand.`, + longPrompt: `Does this person have a ${WEBID_NOUN}?`, idNoun: WEBID_NOUN, urlType: ns.vcard('WebID') } @@ -182,13 +174,11 @@ export async function renderPublicIdControl (person, dataBrowserContext) { if (kb.holds(person, ns.rdf('type'), ns.schema(classId), person.doc())) { orgClass = kb.sym(wikidataClasses[classId]) orgClassId = classId - console.log(` renderPublicIdControl bingo: ${classId} -> ${orgClass}`) + debug.log(` renderPublicIdControl bingo: ${classId} -> ${orgClass}`) } } const options = { - longPrompt: `If you know the ${PUBLICID_NOUN} of this ${orgClassId}, you can do more stuff with it. - To record its ${PUBLICID_NOUN}, drag it onto the plus, or click the magnifyinng glass - to search for it in WikiData.`, + longPrompt: `Does this ${orgClassId} have a ${PUBLICID_NOUN}?`, idNoun: PUBLICID_NOUN, urlType: ns.vcard('PublicId'), dbLookup: true, @@ -209,8 +199,10 @@ export async function renderIdControl (person, dataBrowserContext, options) { function renderPersona (dom, persona, kb) { function profileOpenHandler (_event) { profileIsVisible = !profileIsVisible - main.style.visibility = profileIsVisible ? 'visible' : 'collapse' + main.classList.toggle('collapsed', !profileIsVisible) openButton.children[0].src = profileIsVisible ? UP_ARROW : DOWN_ARROW // @@ fragile + openButton.setAttribute('aria-expanded', profileIsVisible ? 'true' : 'false') + openButton.setAttribute('aria-label', profileIsVisible ? 'Collapse profile' : 'Expand profile') } function renderNewRow (webidObject) { const webid = new $rdf.Literal(webidObject.uri) @@ -232,18 +224,18 @@ export async function renderIdControl (person, dataBrowserContext, options) { const row = widgets.personTR(dom, UI.ns.foaf('knows'), webidObject, opts) if (isWebId) { row.children[1].textConent = opts.title // @@ will be overwritten - row.style.backgroundColor = webidPanelBackgroundColor + row.classList.add('personaRow--webid') } - row.style.padding = '0.2em' + row.classList.add('personaRow') return row } const div = dom.createElement('div') - div.style.width = '100%' + div.classList.add('fullWidth') const personaTable = div.appendChild(dom.createElement('table')) - personaTable.style.width = '100%' + personaTable.classList.add('fullWidth') const nav = personaTable.appendChild(renderNewRow(persona)) - nav.style.width = '100%' + nav.classList.add('fullWidth') const mainRow = personaTable.appendChild(dom.createElement('tr')) const mainCell = mainRow.appendChild(dom.createElement('td')) mainCell.setAttribute('colspan', 3) @@ -253,18 +245,18 @@ export async function renderIdControl (person, dataBrowserContext, options) { const rhs = nav.children[2] const openButton = rhs.appendChild(widgets.button(dom, DOWN_ARROW, 'View', profileOpenHandler)) - openButton.style.float = 'right' - delete openButton.style.backgroundColor - delete openButton.style.border + openButton.classList.add('personaOpenButton') + openButton.setAttribute('aria-expanded', 'true') + openButton.setAttribute('aria-label', 'Collapse profile') const paneName = isOrganization(person) || isOrganization(persona) ? 'profile' : 'profile' // was default for org widgets.publicData.loadPublicDataThing(kb, person, persona).then(_resp => { // loadPublicDataThing(kb, person, persona).then(_resp => { try { main = renderNamedPane(dom, persona, paneName, dataBrowserContext) - console.log('main: ', main) - main.style.width = '100%' - console.log('renderIdControl: main element: ', main) + debug.log('main: ', main) + main.classList.add('fullWidth') + debug.log('renderIdControl: main element: ', main) // main.style.visibility = 'collapse' mainCell.appendChild(main) } catch (err) { @@ -280,8 +272,8 @@ export async function renderIdControl (person, dataBrowserContext, options) { async function refreshWebIDTable () { const personas = getPersonas(kb, person) - console.log('WebId personas: ' + person + ' -> ' + personas.map(p => p.uri).join(',\n ')) - prompt.style.display = personas.length ? 'none' : '' + debug.log('WebId personas: ' + person + ' -> ' + personas.map(p => p.uri).join(',\n ')) + prompt.classList.toggle('hidden', personas.length > 0) utils.syncTableToArrayReOrdered(profileArea, personas, persona => renderPersona(dom, persona, kb)) } async function addOneIdAndRefresh (person, webid) { @@ -297,29 +289,40 @@ export async function renderIdControl (person, dataBrowserContext, options) { options = options || {} options.editable = kb.updater.editable(person.doc().uri, kb) const div = dom.createElement('div') - div.style = 'border-radius:0.3em; border: 0.1em solid #888;' // padding: 0.8em; + div.classList.add('webidControl') if (getPersonas(kb, person).length === 0 && !options.editable) { - div.style.display = 'none' + div.classList.add('hidden') return div // No point listing an empty list you can't change } - const h4 = div.appendChild(dom.createElement('h4')) - h4.textContent = options.idNoun - h4.style = style.formHeadingStyle - h4.style.color = style.highlightColor + const h3 = div.appendChild(dom.createElement('h3')) + h3.textContent = options.idNoun + h3.classList.add('webidHeading') const prompt = div.appendChild(dom.createElement('p')) - prompt.style = style.commentStyle + prompt.classList.add('webidPrompt') prompt.textContent = options.longPrompt const table = div.appendChild(dom.createElement('table')) - table.style.width = '100%' + table.classList.add('fullWidth') if (options.editable) { // test - options.manualURIEntry = true // introduced in solid-ui 2.4.2 - options.queryParams = options.queryParams || wikidataParameters - div.appendChild(await renderAutocompleteControl(dom, person, options, addOneIdAndRefresh)) - // div.appendChild(await widgets.renderAutocompleteControl(dom, person, options, addOneIdAndRefresh)) + const barOptions = { + editable: options.editable, + manualURIEntry: true, // introduced in solid-ui 2.4.2 + idNoun: options.idNoun, + dbLookup: options.dbLookup + } + const acOptions = { + queryParams: options.queryParams || wikidataParameters, + targetClass: options.class + } + try { + div.appendChild(await widgets.renderAutocompleteControl(dom, person, barOptions, acOptions, addOneIdAndRefresh)) + } catch (err) { + debug.error('renderAutocompleteControl failed:', err) + div.appendChild(widgets.errorMessageBlock(dom, 'Error rendering autocomplete: ' + err)) + } } const profileArea = div.appendChild(dom.createElement('div')) await refreshWebIDTable() diff --git a/test/__mocks__/styleMock.js b/test/__mocks__/styleMock.js new file mode 100644 index 0000000..4ba52ba --- /dev/null +++ b/test/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/test/unit/data-reformat-test.test.ts b/test/unit/data-reformat-test.test.ts index 38810d8..e5e5d79 100644 --- a/test/unit/data-reformat-test.test.ts +++ b/test/unit/data-reformat-test.test.ts @@ -114,7 +114,7 @@ if (t[ns.vcard('AddressBook').uri]) return 'Address book' it('returns a good label contact if Organization', () => { const thing = sym(base + 'thing1') store.add(thing, ns.rdf('type'), ns.vcard('Organization'), doc) - expect(pane.label(thing, context)).toEqual('contact') + expect(pane.label(thing, context)).toEqual('Contact') }) it('returns a good label Contact for Individual', () => { diff --git a/webpack.config.mjs b/webpack.config.mjs new file mode 100644 index 0000000..786cd76 --- /dev/null +++ b/webpack.config.mjs @@ -0,0 +1,120 @@ +import path from 'path' +import TerserPlugin from 'terser-webpack-plugin' +import CopyPlugin from 'copy-webpack-plugin' + +const common = { + entry: './src/contactsPane.js', + resolve: { + extensions: ['.js'], + }, + module: { + rules: [ + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.ttl$/i, + type: 'asset/source' + } + ], + }, + externals: { + fs: 'null', + 'node-fetch': 'fetch', + 'isomorphic-fetch': 'fetch', + 'text-encoding': 'TextEncoder', + '@trust/webcrypto': 'crypto', + rdflib: { + commonjs: 'rdflib', + commonjs2: 'rdflib', + amd: 'rdflib', + root: '$rdf' + }, + 'solid-logic': { + commonjs: 'solid-logic', + commonjs2: 'solid-logic', + amd: 'solid-logic', + root: 'SolidLogic' + }, + 'solid-ui': { + commonjs: 'solid-ui', + commonjs2: 'solid-ui', + amd: 'solid-ui', + root: 'UI' + } + }, + devtool: 'source-map', +} + +const normalConfig = { + ...common, + mode: 'production', + output: { + path: path.resolve(process.cwd(), 'dist'), + filename: 'contactsPane.js', + library: { + type: 'umd', + name: 'ContactsPane', + export: 'default', + }, + globalObject: 'globalThis', + clean: true, + }, + plugins: [ + ...(common.plugins || []), + new CopyPlugin({ + patterns: [ + { + from: path.resolve('src/styles'), + to: path.resolve('dist/styles'), + }, + ], + }), + ], + optimization: { + minimize: false, + } +} + +const minConfig = { + ...common, + mode: 'production', + output: { + path: path.resolve(process.cwd(), 'dist'), + filename: 'contactsPane.min.js', + library: { + type: 'umd', + name: 'ContactsPane', + export: 'default', + }, + globalObject: 'globalThis', + clean: false, + }, + plugins: [ + ...(common.plugins || []), + new CopyPlugin({ + patterns: [ + { + from: path.resolve('src/styles'), + to: path.resolve('dist/styles'), + }, + ], + }), + ], + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + format: { + comments: false, + }, + }, + extractComments: false, + }) + ], + } +} + +export default [normalConfig, minConfig] diff --git a/webpack.dev.config.mjs b/webpack.dev.config.mjs new file mode 100644 index 0000000..ab4713d --- /dev/null +++ b/webpack.dev.config.mjs @@ -0,0 +1,85 @@ +import HtmlWebpackPlugin from 'html-webpack-plugin' +import NodePolyfillPlugin from 'node-polyfill-webpack-plugin' +import webpack from 'webpack' + +export default [ + { + mode: 'development', + entry: ['./dev/index.ts'], + plugins: [ + new HtmlWebpackPlugin({ + template: './dev/index.html', + inject: 'head' + }), + new NodePolyfillPlugin(), + new webpack.ProvidePlugin({ + $rdf: 'rdflib', + SolidLogic: 'solid-logic', + UI: 'solid-ui' + }), + new webpack.DefinePlugin({ + global: 'globalThis', + 'process.env.NODE_ENV': JSON.stringify('development') + }) + ], + module: { + rules: [ + { + test: /\.(js|ts)$/, + exclude: /node_modules/, + use: ['babel-loader'], + }, + { + test: /\.ttl$/, // Target text files + type: 'asset/source', // Load the file's content as a string + }, + { + test: /\.css$/, + exclude: /\.module\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.module\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true + } + } + ] + } + ], + }, + resolve: { + extensions: ['.js', '.ts'], + alias: { + $rdf: 'rdflib', + rdflib: 'rdflib', + SolidLogic: 'solid-logic', + 'solid-logic': 'solid-logic', + UI: 'solid-ui', + 'solid-ui': 'solid-ui' + } + }, + output: { + globalObject: 'globalThis', + library: { + type: 'umd', + umdNamedDefine: true + } + }, + optimization: { + usedExports: true, + // Tree shaking in development (normally disabled for faster builds) + providedExports: true, + }, + devServer: { + static: [ + './dev' + ], + }, + devtool: 'source-map', + }, +]