├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── VERSION ├── _build.ts ├── bond-directive.ts ├── constructor.ts ├── deno.json ├── deno.lock ├── deps.ts ├── examples ├── .DS_Store ├── counter │ ├── README.md │ ├── _build.ts │ ├── counter.ts │ ├── counter_test.ts │ ├── deno.json │ └── deno.lock └── todo │ ├── README.md │ ├── _build.ts │ ├── deno.json │ ├── deno.lock │ ├── deps.ts │ ├── index.html │ ├── main.ts │ ├── todo-app.ts │ ├── todo-item.ts │ └── todo.ts ├── local-repository.ts ├── mod.ts ├── observable.ts ├── observer.ts ├── repository.ts ├── rest-repository.ts ├── result.ts └── tests ├── bond-directive_test.ts ├── dom.ts ├── local-repository_test.ts ├── mod_test.ts ├── observable_test.ts ├── observer_test.ts ├── rest-repository_test.ts └── test_deps.ts /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow 2 | name: ci 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: download deno 11 | uses: denoland/setup-deno@main 12 | with: 13 | deno-version: 1.34.0 14 | - name: check format 15 | run: deno fmt --check 16 | - name: lint 17 | run: deno lint 18 | - name: run tests 19 | run: deno task test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | coverage 3 | npm -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 Maga D. Zandaqo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compago 2 | 3 | [![Actions Status](https://github.com/zandaqo/compago/workflows/ci/badge.svg)](https://github.com/zandaqo/compago/actions) 4 | [![npm](https://img.shields.io/npm/v/compago.svg?style=flat-square)](https://www.npmjs.com/package/compago) 5 | 6 | Compago is a minimalist framework inspired by Domain-Driven Design for building 7 | applications using Web Components. 8 | 9 | Although most components are isomorphic and can be used independently, Compago 10 | works best with [Lit](https://lit.dev) for which it offers extensions focused on 11 | simplifying advanced state management in web components. 12 | 13 | - [Installation](#installation) 14 | - [State Management with Observables](#state-management-with-observables) 15 | - [Quick Example](#quick-example) 16 | - [Observables](#observables) 17 | - [Using with Lit](#using-with-lit) 18 | - [Data Binding](#data-binding) 19 | - [Shared State](#shared-state) 20 | - [State Persistance with Repositories](#state-persistance-with-repositories) 21 | 22 | ## Installation 23 | 24 | Node.js: 25 | 26 | ```bash 27 | npm i compago 28 | ``` 29 | 30 | ```js 31 | import { ... } from "compago"; 32 | ``` 33 | 34 | Components can also be imported separately, to avoid loading extra dependencies, 35 | for example: 36 | 37 | ```js 38 | import { Result } from "compago/result"; 39 | import { Observable } from "compago/observable"; 40 | ``` 41 | 42 | Deno: 43 | 44 | ```js 45 | import { ... } from "https://raw.githubusercontent.com/zandaqo/compago/master/mod.ts" 46 | ``` 47 | 48 | ## State Management with Observables 49 | 50 | UI frameworks like React and Lit provide built-in mechanisms to deal with simple 51 | UI state expressed as primitive values such as `useState` hooks in React or 52 | Lit's reactive properties. However, those mechanisms are cumbersome to work with 53 | when dealing with a complex domain state that involves nested objects and 54 | arrays. To give reactivity to complex domain objects, Compago introduces the 55 | `Observable` class. Observable wraps a given domain object into a proxy that 56 | reacts to changes on the object and all its nested structures with a `change` 57 | event. When used in Lit elements, additional helpers like `@observer`, 58 | `@observe` decorators and `bond` directive make reactivity and data binding 59 | seamless. 60 | 61 | ### Quick Example 62 | 63 | ```typescript 64 | import { html, LitElement } from "lit"; 65 | import { bond, Observable, observe, observer } from "compago"; 66 | 67 | class Todo { 68 | description = ""; 69 | done = false; 70 | } 71 | 72 | @observer() 73 | class TodoItem extends LitElement { 74 | // Create an observable of a Todo object tied to the element. 75 | // The observable will be treated as internal state and updates within the observable 76 | // (and all nested objects) will be propagated 77 | // through the usual lifecycle of reactive properties. 78 | @observe() 79 | state = new Observable(new Todo()); 80 | 81 | // You can hook into updates to the observable properties just like reactive ones. 82 | // Names for the nested properties are given as a path, e.g. `state.done`, `state.description` 83 | protected updated(changedProperties: Map) { 84 | // check if the description of the todo has changed 85 | if (changedProperties.has("state.description")) { 86 | console.log("Todo description has changed!"); 87 | } 88 | } 89 | 90 | render() { 91 | return html` 92 |
93 | 95 | @input=${bond({ to: this.state, key: "description" })} /> 96 | 98 | @click=${ 99 | bond({ to: this.state, key: "done", attirubute: "checked" }) 100 | } /> 101 |
102 | `; 103 | } 104 | } 105 | ``` 106 | 107 | ### Observables 108 | 109 | `Observable` makes monitoring changes on JavaScript objects as seamless as 110 | possible using the built-in Proxy and EventTarget interfaces. Observable wraps a 111 | given object into a proxy that emits `change` events whenever a change happens 112 | to the object or its "nested" objects and arrays. Since `Observable` is an 113 | extension of DOM's EventTarget, listening to changes is done through the 114 | standard event handling mechanisms. 115 | 116 | ```ts 117 | import { Observable } from "compago"; 118 | 119 | class Todo { 120 | description = "..."; 121 | done = false; 122 | } 123 | 124 | const todo = new Observable(new Todo()); 125 | 126 | todo.addEventListener("change", (event) => { 127 | console.log(event); 128 | }); 129 | 130 | todo.done; 131 | //=> false 132 | 133 | todo.done = true; 134 | //=> ChangeEvent { 135 | //=> type: 'change', 136 | //=> kind: 'SET' 137 | //=> path: '.done' 138 | //=> previous: false, 139 | //=> defaultPrevented: false, 140 | //=> cancelable: false, 141 | //=> timeStamp: ... 142 | //=>} 143 | //=> true 144 | 145 | todo.done; 146 | //=> true 147 | 148 | JSON.stringify(todo); 149 | //=> { "description": "...", "done": true } 150 | ``` 151 | 152 | Observable only monitors own, public, enumerable, non-symbolic properties, thus, 153 | any other sort of properties (i.e. private, symbolic, or non-enumerable) can be 154 | used to manipulate data without triggering `change` events, e.g. to store 155 | computed properties. 156 | 157 | ### Using with Lit 158 | 159 | Observables complement Lit element's reactive properties allowing separation of 160 | complex domain state (held in observables) and UI state (held in properties). To 161 | simplify working with observables, Compago offers `ObserverElement` mixin or 162 | `observer` and `observe` decorators that handle attaching and detaching from 163 | observables and triggering LitElement updates when observables detect changes. 164 | 165 | ```ts 166 | import { observer, observe } from 'compago'; 167 | 168 | class Comment { 169 | id = 0; 170 | text = ''; 171 | meta = { 172 | author: ''; 173 | date: new Date(); 174 | } 175 | } 176 | 177 | @observer() 178 | @customElement('comment-element'); 179 | class CommentElement extends LitElement { 180 | // Set up `comment` property to hold a reference to an observable 181 | // holding a domain state. Any change to the comment will trigger 182 | // the elements update cycle. 183 | @observe() comment = new Observable(new Comment()); 184 | @property({ type: Boolean }) isEditing = false; 185 | ... 186 | 187 | updated(changedProperties: PropertyValues): void { 188 | if (changedProperties.has('comment.text')) { 189 | // react if the comment's text property has changed 190 | } 191 | } 192 | } 193 | ``` 194 | 195 | While decorators are quite popular in the Lit world, you can achieve the same 196 | effect without them by using `ObserverMixin` and specifying list of observables 197 | in a static property: 198 | 199 | ```ts 200 | const CommentElement = ObserverMixin(class extends LitElement { 201 | static observables = ['comment']; 202 | ... 203 | comment = new Observable(new Comment()); 204 | ... 205 | }) 206 | ``` 207 | 208 | ### Data Binding 209 | 210 | We often have to take values from DOM elements (e.g. input fields) and update 211 | our UI or domain states with them. To simplify the process, Compago offers the 212 | `bond` directive. The directive provides a declarative way to define an event 213 | listener that (upon being triggered) takes the specified value from its DOM 214 | element, optionally validates and parses it, and sets it on desired object, be 215 | it the element itself or an observable domain state object. 216 | 217 | ```ts 218 | class CommentElement extends LitElement { 219 | // Set up `comment` property to hold a reference to an observable 220 | // holding a domain state. Any change to the comment will trigger 221 | // the elements update cycle. 222 | @observe() 223 | comment = new Observable(new Comment()); 224 | ... 225 | render() { 226 | return html` 227 |
228 | 231 | @input=${ 232 | bond({ 233 | to: this.comment, 234 | key: "text", 235 | validate: (text) => text.length < 3000, 236 | }) 237 | } /> 238 |
239 | `; 240 | } 241 | } 242 | ``` 243 | 244 | ### Shared State 245 | 246 | Observable are fully independent and can be shared between components. For 247 | example, a parent can share "parts" of an observable with children as shown in 248 | our example [todo app]: 249 | 250 | ```ts 251 | @customElement("todo-app") 252 | @observer() 253 | export class TodoApp extends LitElement { 254 | // Here we create an observable that holds an array of todo objects. 255 | @observe() 256 | state = new Observable({ items: [] as Array }); 257 | ... 258 | render() { 259 | return html` 260 |

Todos Compago & Lit

261 |
262 | ${ 263 | repeat(this.state.items, (todo) => 264 | todo.id, (todo) => 265 | // We supply each todo object to a child element. 266 | // Now when any of the todo objects is changed, it will trigger 267 | // re-render of the todo-item and the parent todo-app, 268 | // but not the sibling todo-items. 269 | html``) 270 | } 271 |
272 | `; 273 | } 274 | } 275 | 276 | @observer() 277 | @customElement("todo-item") 278 | export class TodoItem extends LitElement { 279 | @observe() 280 | todo!: Todo; 281 | 282 | render() { 283 | return html` 284 | 291 | 292 | 293 | `; 294 | } 295 | } 296 | ``` 297 | 298 | Observables can be shared not only between parents and children, but also 299 | between siblings, or the same observable can be shared by different components, 300 | in fact, you can use a single observable to hold all state in your app (as a 301 | "global store"). 302 | 303 | ## State Persistance with Repositories 304 | 305 | A repository is a design pattern of data-centric, domain-driven design that 306 | encapsulates the logic for accessing data sources while abstracting the 307 | underlying persistence technology. Web applications exchange data with a variety 308 | of data sources, such as servers exposed through a RESTful API, local storage 309 | using IndexedDB API, or databases through their respective drivers. Compago 310 | offers a unifying interface for repositories and implementations for RESTful 311 | APIs (`RESTRepository`) and local storage with IndexedDB (`LocalRepository`). 312 | 313 | ```typescript 314 | import { RESTRepository } from 'compago'; 315 | 316 | class Comment { 317 | id = 0; 318 | text = ''; 319 | meta = { 320 | author: ''; 321 | date: new Date(); 322 | } 323 | } 324 | 325 | // Create a repository for comments that will access the API end point `/comments` 326 | const remoteRepository = new RESTRepository(Comment, '/comments', 'id'); 327 | 328 | // retrieve the comment with id 1 329 | // by sending a GET request to `/comments/1` API end-point 330 | const readResult = await remoteRepository.read(1); 331 | 332 | readResult.ok 333 | //=> true if successful 334 | 335 | const comment = readResult.value 336 | //=> Comment {...} 337 | ``` 338 | 339 | ## License 340 | 341 | MIT @ Maga D. Zandaqo 342 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 5.0.5 -------------------------------------------------------------------------------- /_build.ts: -------------------------------------------------------------------------------- 1 | import { 2 | build, 3 | emptyDir, 4 | } from "https://raw.githubusercontent.com/denoland/dnt/0.37.0/mod.ts"; 5 | 6 | const version = await Deno.readTextFile("./VERSION"); 7 | 8 | await emptyDir("npm"); 9 | 10 | await build({ 11 | entryPoints: ["./mod.ts"], 12 | outDir: "./npm", 13 | typeCheck: false, 14 | test: false, 15 | declaration: "inline", 16 | scriptModule: false, 17 | compilerOptions: { 18 | target: "Latest", 19 | sourceMap: true, 20 | inlineSources: true, 21 | lib: ["ES2022", "DOM"], 22 | }, 23 | shims: { 24 | deno: false, 25 | timers: false, 26 | }, 27 | package: { 28 | name: "compago", 29 | version: version.trim(), 30 | main: "mod.js", 31 | type: "module", 32 | description: "A minimalist MVC framework for modern browsers.", 33 | keywords: [ 34 | "framework", 35 | "frontend", 36 | "full-stack", 37 | "lit", 38 | "state", 39 | "reactive", 40 | "DDD", 41 | ], 42 | author: "Maga D. Zandaqo (http://maga.name)", 43 | license: "MIT", 44 | repository: { 45 | type: "git", 46 | url: "https://github.com/zandaqo/compago.git", 47 | }, 48 | homepage: "https://github.com/zandaqo/compago#readme", 49 | bugs: { 50 | url: "https://github.com/zandaqo/compago/issues", 51 | }, 52 | exports: { 53 | ".": { 54 | types: "./esm/mod.d.ts", 55 | import: "./esm/mod.js", 56 | }, 57 | "./*": { 58 | types: "./esm/*.d.ts", 59 | import: "./esm/*.js", 60 | }, 61 | }, 62 | }, 63 | }); 64 | 65 | Deno.copyFileSync("LICENSE", "npm/LICENSE"); 66 | Deno.copyFileSync("README.md", "npm/README.md"); 67 | -------------------------------------------------------------------------------- /bond-directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, directive, PartType } from "./deps.ts"; 2 | import type { EventPart, PartInfo } from "./deps.ts"; 3 | 4 | type BondOptions = { 5 | /** 6 | * An object that receives the value 7 | */ 8 | to: T; 9 | /** 10 | * A property name of the receiving object to set the value 11 | */ 12 | key: K; 13 | /** 14 | * Parse function to apply to the value, gets the bond value as the parameter 15 | */ 16 | parse?: (...args: unknown[]) => T[K]; 17 | /** 18 | * The validator function to check the value 19 | * 20 | * @param element The bound DOM element 21 | * @param content The value being checked 22 | */ 23 | validate?: (element: Element, content: unknown) => boolean; 24 | /** 25 | * Whether to prevent the default event handling behavior 26 | */ 27 | prevent?: boolean; 28 | /** 29 | * Name of the DOM element's property that provides the bond value 30 | */ 31 | property?: string; 32 | /** 33 | * Name of the DOM element's attribute that provides the bond value 34 | */ 35 | attribute?: string; 36 | /** 37 | * The constant value to be set on the bond object 38 | */ 39 | value?: T[K]; 40 | }; 41 | 42 | export type BondFunction = < 43 | T extends object, 44 | K extends keyof T, 45 | >( 46 | options: BondOptions, 47 | ) => unknown; 48 | 49 | export class BondDirective 50 | extends Directive { 51 | options!: BondOptions; 52 | recipient!: T; 53 | property!: K; 54 | element!: Element; 55 | 56 | constructor(partInfo: PartInfo) { 57 | super(partInfo); 58 | if (partInfo.type !== PartType.EVENT) { 59 | throw new Error("bond only supports event expressions"); 60 | } 61 | } 62 | 63 | update( 64 | part: EventPart, 65 | [options]: [BondOptions], 66 | ): unknown { 67 | this.options = options; 68 | this.recipient = options.to || part.element as T; 69 | this.property = options.key; 70 | this.element = part.element; 71 | return this.render(); 72 | } 73 | 74 | render(): unknown { 75 | return this.handler; 76 | } 77 | 78 | handler = (event: Event) => { 79 | const { 80 | parse, 81 | prevent, 82 | property = "value", 83 | attribute, 84 | value, 85 | validate, 86 | } = this.options; 87 | 88 | if (prevent) event.preventDefault(); 89 | let content = Reflect.has(this.options, "value") 90 | ? value 91 | : attribute != null 92 | ? (this.element as HTMLElement).getAttribute(attribute) 93 | : this.element![property as keyof Element]; 94 | if (typeof validate === "function" && !validate(this.element!, content)) { 95 | return; 96 | } 97 | if (typeof parse === "function") content = parse(content); 98 | this.recipient[this.property] = content as T[K]; 99 | }; 100 | } 101 | 102 | /** 103 | * Creates a one-way bond passing a value from a DOM element to 104 | * a JavaScript object or another DOM element every time 105 | * the listening event is fired. 106 | */ 107 | export const bond = directive(BondDirective) as BondFunction; 108 | -------------------------------------------------------------------------------- /constructor.ts: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore no-explicit-any 2 | export type Constructor = { new (...args: any[]): T }; 3 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "dom.asynciterable", 8 | "deno.ns" 9 | ] 10 | }, 11 | "tasks": { 12 | "test": "deno test -A --no-check --location https://example.com ./tests", 13 | "build": "deno run -A --no-check _build.ts" 14 | }, 15 | "lint": { 16 | "rules": { 17 | "exclude": ["ban-types"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://cdn.skypack.dev/-/indexeddbshim@v9.0.0-QVaW8rBIOGlJegwkWTsK/dist=es2020,mode=imports/unoptimized/dist/indexeddbshim-noninvasive.js": "036e9191db93ef296b84a8a59192d4851f231e3b76a638543491cf81fe08b666", 5 | "https://cdn.skypack.dev/-/regenerator-runtime@v0.13.9-4Dxus9nU31cBsHxnWq2H/dist=es2020,mode=imports/optimized/regenerator-runtime.js": "9d5c28ca64c45a9e606d5f3b35aec61568ed451495968171c8cc3ef1923cb7fe", 6 | "https://cdn.skypack.dev/pin/indexeddbshim@v9.0.0-QVaW8rBIOGlJegwkWTsK/mode=imports/unoptimized/dist/indexeddbshim-noninvasive.js": "978a698ccf50ce19735b1da6cad00277cc8fc790610df183c61c2f9cc1df9c9b", 7 | "https://cdn.skypack.dev/pin/regenerator-runtime@v0.13.9-4Dxus9nU31cBsHxnWq2H/mode=imports/optimized/regenerator-runtime.js": "091dd6c5266025b80081cdd5bbadd9f6e03cad950c8a16c7454b58d77b422253", 8 | "https://deno.land/std@0.111.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", 9 | "https://deno.land/std@0.111.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac", 10 | "https://deno.land/std@0.111.0/bytes/bytes_list.ts": "3bff6a09c72b2e0b1e92e29bd3b135053894196cca07a2bba842901073efe5cb", 11 | "https://deno.land/std@0.111.0/bytes/equals.ts": "69f55fdbd45c71f920c1a621e6c0865dc780cd8ae34e0f5e55a9497b70c31c1b", 12 | "https://deno.land/std@0.111.0/bytes/mod.ts": "fedb80b8da2e7ad8dd251148e65f92a04c73d6c5a430b7d197dc39588c8dda6f", 13 | "https://deno.land/std@0.111.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621", 14 | "https://deno.land/std@0.111.0/fs/_util.ts": "f2ce811350236ea8c28450ed822a5f42a0892316515b1cd61321dec13569c56b", 15 | "https://deno.land/std@0.111.0/fs/ensure_dir.ts": "b7c103dc41a3d1dbbb522bf183c519c37065fdc234831a4a0f7d671b1ed5fea7", 16 | "https://deno.land/std@0.111.0/hash/sha256.ts": "bd85257c68d1fdd9da8457284c4fbb04efa9f4f2229b5f41a638d5b71a3a8d5c", 17 | "https://deno.land/std@0.111.0/io/buffer.ts": "fdf93ba9e5d20ff3369e2c42443efd89131f8a73066f7f59c033cc588a0e2cfe", 18 | "https://deno.land/std@0.111.0/io/types.d.ts": "89a27569399d380246ca7cdd9e14d5e68459f11fb6110790cc5ecbd4ee7f3215", 19 | "https://deno.land/std@0.111.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853", 20 | "https://deno.land/std@0.111.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4", 21 | "https://deno.land/std@0.111.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b", 22 | "https://deno.land/std@0.111.0/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4", 23 | "https://deno.land/std@0.111.0/path/glob.ts": "ea87985765b977cc284b92771003b2070c440e0807c90e1eb0ff3e095911a820", 24 | "https://deno.land/std@0.111.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12", 25 | "https://deno.land/std@0.111.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2", 26 | "https://deno.land/std@0.111.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", 27 | "https://deno.land/std@0.111.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e", 28 | "https://deno.land/std@0.111.0/streams/conversion.ts": "fe0059ed9d3c53eda4ba44eb71a6a9acb98c5fdb5ba1b6c6ab28004724c7641b", 29 | "https://deno.land/std@0.136.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 30 | "https://deno.land/std@0.136.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", 31 | "https://deno.land/std@0.136.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 32 | "https://deno.land/std@0.136.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 33 | "https://deno.land/std@0.136.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 34 | "https://deno.land/std@0.136.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 35 | "https://deno.land/std@0.136.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 36 | "https://deno.land/std@0.136.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", 37 | "https://deno.land/std@0.136.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 38 | "https://deno.land/std@0.136.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 39 | "https://deno.land/std@0.136.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 40 | "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 41 | "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 42 | "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", 43 | "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", 44 | "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", 45 | "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", 46 | "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", 47 | "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", 48 | "https://deno.land/std@0.140.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", 49 | "https://deno.land/std@0.140.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", 50 | "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", 51 | "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", 52 | "https://deno.land/std@0.140.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", 53 | "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 54 | "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 55 | "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 56 | "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 57 | "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 58 | "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", 59 | "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 60 | "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 61 | "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 62 | "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", 63 | "https://deno.land/std@0.143.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 64 | "https://deno.land/std@0.143.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 65 | "https://deno.land/std@0.143.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 66 | "https://deno.land/std@0.143.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 67 | "https://deno.land/std@0.143.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 68 | "https://deno.land/std@0.143.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 69 | "https://deno.land/std@0.143.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 70 | "https://deno.land/std@0.143.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", 71 | "https://deno.land/std@0.143.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 72 | "https://deno.land/std@0.143.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 73 | "https://deno.land/std@0.143.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 74 | "https://deno.land/std@0.165.0/_util/asserts.ts": "d0844e9b62510f89ce1f9878b046f6a57bf88f208a10304aab50efcb48365272", 75 | "https://deno.land/std@0.165.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", 76 | "https://deno.land/std@0.165.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", 77 | "https://deno.land/std@0.165.0/fs/_util.ts": "fdc156f897197f261a1c096dcf8ff9267ed0ff42bd5b31f55053a4763a4bae3b", 78 | "https://deno.land/std@0.165.0/fs/empty_dir.ts": "c15a0aaaf40f8c21cca902aa1e01a789ad0c2fd1b7e2eecf4957053c5dbf707f", 79 | "https://deno.land/std@0.165.0/fs/expand_glob.ts": "d08678afa768881b055bdfb5cebe4f089f8db4513a4d2b0bbe748f5870d77ce3", 80 | "https://deno.land/std@0.165.0/fs/walk.ts": "0a754cc4696a15bdb175380a4b7deff3eb65be9768cb11d91a4138beee35c2d7", 81 | "https://deno.land/std@0.165.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 82 | "https://deno.land/std@0.165.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 83 | "https://deno.land/std@0.165.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", 84 | "https://deno.land/std@0.165.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 85 | "https://deno.land/std@0.165.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 86 | "https://deno.land/std@0.165.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", 87 | "https://deno.land/std@0.165.0/path/posix.ts": "6b63de7097e68c8663c84ccedc0fd977656eb134432d818ecd3a4e122638ac24", 88 | "https://deno.land/std@0.165.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 89 | "https://deno.land/std@0.165.0/path/win32.ts": "7cebd2bda6657371adc00061a1d23fdd87bcdf64b4843bb148b0b24c11b40f69", 90 | "https://deno.land/std@0.166.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", 91 | "https://deno.land/std@0.166.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", 92 | "https://deno.land/std@0.166.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", 93 | "https://deno.land/std@0.166.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8", 94 | "https://deno.land/std@0.166.0/testing/mock.ts": "d7ad40139cda87476c4dc1efeffe406bbfb4a5f82b688e0b85bffe9e911526a2", 95 | "https://deno.land/std@0.171.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 96 | "https://deno.land/std@0.171.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 97 | "https://deno.land/std@0.171.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", 98 | "https://deno.land/std@0.171.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 99 | "https://deno.land/std@0.171.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 100 | "https://deno.land/std@0.171.0/fs/expand_glob.ts": "536055845aafc32de7e7a46c3b778a741825d5e2ed8580d9851a01ec7a5adf2e", 101 | "https://deno.land/std@0.171.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 102 | "https://deno.land/std@0.171.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 103 | "https://deno.land/std@0.171.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 104 | "https://deno.land/std@0.171.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8", 105 | "https://deno.land/std@0.171.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 106 | "https://deno.land/std@0.171.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 107 | "https://deno.land/std@0.171.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232", 108 | "https://deno.land/std@0.171.0/path/posix.ts": "2ecc259e6f34013889b7638ff90339a82d8178f629155761ce6001e41af55a43", 109 | "https://deno.land/std@0.171.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 110 | "https://deno.land/std@0.171.0/path/win32.ts": "99170a0eb0e2b1ce41499c1e86bb55320cb6606299ad947f57ee0a291cdb93d5", 111 | "https://deno.land/std@0.176.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", 112 | "https://deno.land/std@0.176.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 113 | "https://deno.land/std@0.176.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 114 | "https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab", 115 | "https://deno.land/std@0.176.0/testing/mock.ts": "220ed9b8151cb2cac141043d4cfea7c47673fab5d18d1c1f0943297c8afb5d13", 116 | "https://deno.land/std@0.181.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 117 | "https://deno.land/std@0.181.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 118 | "https://deno.land/std@0.181.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 119 | "https://deno.land/std@0.181.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", 120 | "https://deno.land/std@0.181.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", 121 | "https://deno.land/std@0.181.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 122 | "https://deno.land/std@0.181.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 123 | "https://deno.land/std@0.181.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 124 | "https://deno.land/std@0.181.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", 125 | "https://deno.land/std@0.181.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 126 | "https://deno.land/std@0.181.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 127 | "https://deno.land/std@0.181.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", 128 | "https://deno.land/std@0.181.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", 129 | "https://deno.land/std@0.181.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 130 | "https://deno.land/std@0.181.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", 131 | "https://deno.land/std@0.182.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 132 | "https://deno.land/std@0.182.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 133 | "https://deno.land/std@0.182.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 134 | "https://deno.land/std@0.182.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 135 | "https://deno.land/std@0.182.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 136 | "https://deno.land/std@0.182.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", 137 | "https://deno.land/std@0.182.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", 138 | "https://deno.land/std@0.182.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 139 | "https://deno.land/std@0.182.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 140 | "https://deno.land/std@0.182.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", 141 | "https://deno.land/std@0.182.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 142 | "https://deno.land/std@0.182.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 143 | "https://deno.land/std@0.182.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", 144 | "https://deno.land/std@0.182.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", 145 | "https://deno.land/std@0.182.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 146 | "https://deno.land/std@0.182.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", 147 | "https://deno.land/std@0.190.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 148 | "https://deno.land/std@0.190.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 149 | "https://deno.land/std@0.190.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 150 | "https://deno.land/std@0.190.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f", 151 | "https://deno.land/std@0.190.0/testing/mock.ts": "220ed9b8151cb2cac141043d4cfea7c47673fab5d18d1c1f0943297c8afb5d13", 152 | "https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", 153 | "https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", 154 | "https://deno.land/x/code_block_writer@12.0.0/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", 155 | "https://deno.land/x/code_block_writer@12.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", 156 | "https://deno.land/x/deno_cache@0.2.1/auth_tokens.ts": "01b94d25abd974153a3111653998b9a43c66d84a0e4b362fc5f4bbbf40a6e0f7", 157 | "https://deno.land/x/deno_cache@0.2.1/cache.ts": "67e301c20161546fea45405316314f4c3d85cc7a367b2fb72042903f308f55b7", 158 | "https://deno.land/x/deno_cache@0.2.1/deno_dir.ts": "e4dc68da5641aa337bcc06fb1df28fcb086b366dcbea7d8aaed7ac7c853fedb1", 159 | "https://deno.land/x/deno_cache@0.2.1/deps.ts": "2ebaba0ad86fff8b9027c6afd4c3909a17cd8bf8c9e263151c980c15c56a18ee", 160 | "https://deno.land/x/deno_cache@0.2.1/dirs.ts": "e07003fabed7112375d4a50040297aae768f9d06bb6c2655ca46880653b576b4", 161 | "https://deno.land/x/deno_cache@0.2.1/disk_cache.ts": "d7a361f0683a032bcca28513a7bbedc28c77cfcc6719e6f6cea156c0ff1108df", 162 | "https://deno.land/x/deno_cache@0.2.1/file_fetcher.ts": "352702994c190c45215f3b8086621e117e88bc2174b020faefb5eca653d71d6a", 163 | "https://deno.land/x/deno_cache@0.2.1/http_cache.ts": "af1500149496e2d0acadec24569e2a9c86a3f600cceef045dcf6f5ce8de72b3a", 164 | "https://deno.land/x/deno_cache@0.2.1/mod.ts": "709ab9d1068be5fd77b020b33e7a9394f1e9b453553b1e2336b72c90283cf3c0", 165 | "https://deno.land/x/deno_cache@0.2.1/util.ts": "652479928551259731686686ff2df6f26bc04e8e4d311137b2bf3bc10f779f48", 166 | "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", 167 | "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", 168 | "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", 169 | "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", 170 | "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", 171 | "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", 172 | "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", 173 | "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", 174 | "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", 175 | "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", 176 | "https://deno.land/x/deno_graph@0.26.0/lib/deno_graph.generated.js": "2f7ca85b2ceb80ec4b3d1b7f3a504956083258610c7b9a1246238c5b7c68f62d", 177 | "https://deno.land/x/deno_graph@0.26.0/lib/loader.ts": "380e37e71d0649eb50176a9786795988fc3c47063a520a54b616d7727b0f8629", 178 | "https://deno.land/x/deno_graph@0.26.0/lib/media_type.ts": "222626d524fa2f9ebcc0ec7c7a7d5dfc74cc401cc46790f7c5e0eab0b0787707", 179 | "https://deno.land/x/deno_graph@0.26.0/lib/snippets/deno_graph-de651bc9c240ed8d/src/deno_apis.js": "41192baaa550a5c6a146280fae358cede917ae16ec4e4315be51bef6631ca892", 180 | "https://deno.land/x/deno_graph@0.26.0/lib/types.d.ts": "2bbdbf895321d1df8db511fab00160a0211c09c2e7cac56c522dd6e9ed6d2ef7", 181 | "https://deno.land/x/deno_graph@0.26.0/mod.ts": "11131ae166580a1c7fa8506ff553751465a81c263d94443f18f353d0c320bc14", 182 | "https://deno.land/x/deno_graph@0.6.0/lib/deno_graph.generated.js": "3e1cccd6376d4ad0ea789d66aa0f6b19f737fa8da37b5e6185ef5c269c974f54", 183 | "https://deno.land/x/deno_graph@0.6.0/lib/loader.ts": "13a11c1dea0d85e0ad211be77217b8c06138bbb916afef6f50a04cca415084a9", 184 | "https://deno.land/x/deno_graph@0.6.0/lib/media_type.ts": "36be751aa63d6ae36475b90dca5fae8fd7c3a77cf13684c48cf23a85ee607b31", 185 | "https://deno.land/x/deno_graph@0.6.0/lib/snippets/deno_graph-1c138d6136337537/src/deno_apis.js": "f13f2678d875372cf8489ceb7124623a39fa5bf8de8ee1ec722dbb2ec5ec7845", 186 | "https://deno.land/x/deno_graph@0.6.0/lib/types.d.ts": "68cb232e02a984658b40ffaf6cafb979a06fbfdce7f5bd4c7a83ed1a32a07687", 187 | "https://deno.land/x/deno_graph@0.6.0/mod.ts": "8fe3d39bdcb273adfb41a0bafbbaabec4c6fe6c611b47fed8f46f218edb37e8e", 188 | "https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66", 189 | "https://deno.land/x/indexeddb@1.3.5/lib/indexeddb.ts": "6d8eeec0c4074ed2e3ed5ae3c99e3b66703f772c791279d052680fe3b334de2d", 190 | "https://deno.land/x/indexeddb@1.3.5/lib/indexeddb_global.ts": "7bec428dcf95b36a61b8acec5b5c943fb33a86815bff57458ada59e1642dfbe3", 191 | "https://deno.land/x/indexeddb@1.3.5/lib/shim.ts": "8b98a9395134220ac9dfe7314c3e27f37feffec0cab37dbf557f06f118b6eaad", 192 | "https://deno.land/x/indexeddb@1.3.5/polyfill_memory.ts": "83911d846564fe689356bd08054ccb994405b82d3167807f2129a7bb558f0498", 193 | "https://deno.land/x/sqlite@v3.3.0/build/sqlite.d.ts": "b52e6b5ea2e0517750be2ea1eae8da0edd5056c1f285247aa1c0153f4ef504ec", 194 | "https://deno.land/x/sqlite@v3.3.0/build/sqlite.js": "0b74712a5db206aa399f9833c40fd08eb24dfaff5dee77749e4bcadb333ec676", 195 | "https://deno.land/x/sqlite@v3.3.0/build/vfs.js": "baff72655c0916c906327fe6703c6a47daa1346e55c2eaa2629bcd879a673c8d", 196 | "https://deno.land/x/sqlite@v3.3.0/mod.ts": "eb55ff3e103826ee735fa1a8ec2db6e96c8fec826faf198239c66a0fe635c4b6", 197 | "https://deno.land/x/sqlite@v3.3.0/src/constants.ts": "b967237b460fafa03ca03aeddb33953bed41c7cbdc309a934e2329a5c84079ac", 198 | "https://deno.land/x/sqlite@v3.3.0/src/db.ts": "59b9c4fbe733da0e512dcd6b1f1f6a5ac8b165b565505670e9b81a73803dd36a", 199 | "https://deno.land/x/sqlite@v3.3.0/src/error.ts": "abf9df169951faf8c21a68d99ef9efd257c788c19784f27dd147507fbdfe5fe7", 200 | "https://deno.land/x/sqlite@v3.3.0/src/query.ts": "df4d3d6b23d52b36243dd47c07ed6ed7cd23f7834a0f6a2f2c6dbffba63d4c28", 201 | "https://deno.land/x/sqlite@v3.3.0/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487", 202 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", 203 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.d.ts": "607e651c5ae5aa57c2ac4090759a6379e809c0cdc42114742ac67353b1a75038", 204 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.js": "91a954daa993c5acb3361aa5279394f81ea6fe18b3854345c86111b336491cfc", 205 | "https://deno.land/x/ts_morph@17.0.1/common/DenoRuntime.ts": "537800e840d0994f9055164e11bf33eadf96419246af0d3c453793c3ae67bdb3", 206 | "https://deno.land/x/ts_morph@17.0.1/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", 207 | "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.d.ts": "ee7767b0c68b23c65bb607c94b6cb3512e8237fbcb7d1d8383a33235cde2c068", 208 | "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.js": "49a79124b941ba2b35d81ac9eb90fc33c957b2640cdb97569c1941bac5a3bbdb", 209 | "https://deno.land/x/ts_morph@17.0.1/common/typescript.d.ts": "57e52a0882af4e835473dda27e4316cc31149866970210f9f79b940e916b7838", 210 | "https://deno.land/x/ts_morph@17.0.1/common/typescript.js": "5dd669eb199ee2a539924c63a92e23d95df43dfe2fbe3a9d68c871648be1ad5e", 211 | "https://deno.land/x/ts_morph@18.0.0/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", 212 | "https://deno.land/x/ts_morph@18.0.0/bootstrap/ts_morph_bootstrap.d.ts": "607e651c5ae5aa57c2ac4090759a6379e809c0cdc42114742ac67353b1a75038", 213 | "https://deno.land/x/ts_morph@18.0.0/bootstrap/ts_morph_bootstrap.js": "6645ac03c5e6687dfa8c78109dc5df0250b811ecb3aea2d97c504c35e8401c06", 214 | "https://deno.land/x/ts_morph@18.0.0/common/DenoRuntime.ts": "6a7180f0c6e90dcf23ccffc86aa8271c20b1c4f34c570588d08a45880b7e172d", 215 | "https://deno.land/x/ts_morph@18.0.0/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", 216 | "https://deno.land/x/ts_morph@18.0.0/common/ts_morph_common.d.ts": "42a92b8263878ef48b60042dbb55adda3face9abdb8d503be4b4f0fe242f25f4", 217 | "https://deno.land/x/ts_morph@18.0.0/common/ts_morph_common.js": "845671ca951073400ce142f8acefa2d39ea9a51e29ca80928642f3f8cf2b7700", 218 | "https://deno.land/x/ts_morph@18.0.0/common/typescript.d.ts": "21c0786dddf52537611499340166278507eb9784628d321c2cb6acc696cba0f6", 219 | "https://deno.land/x/ts_morph@18.0.0/common/typescript.js": "d5c598b6a2db2202d0428fca5fd79fc9a301a71880831a805d778797d2413c59", 220 | "https://deno.land/x/wasmbuild@0.13.0/cache.ts": "89eea5f3ce6035a1164b3e655c95f21300498920575ade23161421f5b01967f4", 221 | "https://deno.land/x/wasmbuild@0.13.0/loader.ts": "d98d195a715f823151cbc8baa3f32127337628379a02d9eb2a3c5902dbccfc02", 222 | "https://deno.land/x/websql@1.3.2/deps.ts": "2bda07c0ee0eae5f23b5dde415c983c59fb945256f5a9026402eb9118945c025", 223 | "https://deno.land/x/websql@1.3.2/mod.ts": "e7a44b9f376761e5592facd30ed27acae47633275a32eed57cd64f16a461222f", 224 | "https://deno.land/x/websql@1.3.2/src/Database.ts": "ed5f8ae33165d2dd9c3ea1be0b427377628f9437bfe49bfabc32efb3908dc1d5", 225 | "https://deno.land/x/websql@1.3.2/src/SQLResultSet.ts": "fcd83582439c0032794fa26bbbe9e47790467fa5ef1cba1c8a9fddb334f23bf1", 226 | "https://deno.land/x/websql@1.3.2/src/SQLResultSetRowList.ts": "d13bda459cc73bc7e4d596a0411f1e4647d1204a8fddfd42a51720a20d4bf562", 227 | "https://deno.land/x/websql@1.3.2/src/SQLTransaction.ts": "bda17665d54793976957af94a300834e5f07ee546d67e919f14da2a7f77fc797", 228 | "https://deno.land/x/websql@1.3.2/src/SQLiteDBCache.ts": "0c5a67598e39e54c317b40fff3ffc9945e5a9ed0b0c91b5d8a2b379d59c4f4a4", 229 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/compiler.ts": "66312edd9d2544b6bc11dd0e9a615c6d0bf1beed7c9f9851521788009a9c04c9", 230 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/compiler_transforms.ts": "316c24175fe6a5d7ac6bb1dd44d14ef8010ea5773a3ac918db4d64f986402d8b", 231 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/mod.deps.ts": "c25911ebaf412c90dbfaf6b4d8afecb644dc3aa1927b7f10329e68080f3172a1", 232 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/npm_ignore.ts": "ec1a4ca32c5c652c931635632c8fe0e5c8d6c20ab33518a3038fde9163f6da95", 233 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/package_json.ts": "ed633978110d9fc2b1e316986c8c0ac83d56e97793c1ab5842afdfea35c7dc59", 234 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/pkg/dnt_wasm.generated.js": "01dee649eb64416708be7caa720825068a8ee3bea44cccd8b1b320c7a38633ce", 235 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "2f623f83602d4fbb30caa63444b10e35b45e9c2b267e49585ec9bb790a4888d8", 236 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/shims.ts": "a7bc7c5d43e067b12342be883205e694de1ed82c08d48f6a6037b62c23bff169", 237 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/test_runner/get_test_runner_code.ts": "b3682b1a34fc39840dd765e993523efdc97e004186c631de6a2d1c6cec3ebe45", 238 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/test_runner/test_runner.ts": "424589c9db2d5bbcb5c37190fe2676c9a9239e5a6ae8c4c4c22294e376b5de6d", 239 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/transform.deps.ts": "c0c3cb56f4dbda74aa6f58d9ae9e8d870accc8d41647396f2634b0a14027b393", 240 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/types.ts": "8506b5ced3921a6ac2a1d5a2bb381bfdbf818c68207f14a1a1fffbf48ee95886", 241 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/lib/utils.ts": "d2681d634dfa6bd4ad2a32ad15bd419f6f1f895e06c0bf479455fbf1c5f49cd9", 242 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/mod.ts": "837d24f488fb2f80d4ec6882e85ca0bd41bcb351d23b44132d3840ef2eff8069", 243 | "https://raw.githubusercontent.com/denoland/dnt/0.32.0/transform.ts": "a5f81749ecc145af0730e348d5bb3b9b9c7b922822453561feecbdb0d5462b34", 244 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/compiler.ts": "dd589db479d6d7e69999865003ab83c41544e251ece4f21f2f2ee74557097ba6", 245 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/compiler_transforms.ts": "cbb1fd5948f5ced1aa5c5aed9e45134e2357ce1e7220924c1d7bded30dcd0dd0", 246 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/mod.deps.ts": "6648fb17b4a49677cb0c24f60ffb5067a86ad69ff97712d40fe0d62b281b1811", 247 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/npm_ignore.ts": "ddc1a7a76b288ca471bf1a6298527887a0f9eb7e25008072fd9c9fa9bb28c71a", 248 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/package_json.ts": "2d629dbaef8004971e38ce3661f04b915a35342b905c3d98ff4a25343c2a8293", 249 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/pkg/dnt_wasm.generated.js": "00257fc2f03321bb5f2b9bc23cb85e79fe55eb49a325d5ab925b9fc81b4aa963", 250 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be", 251 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/shims.ts": "7998851b149cb230f5183590d3b66c06f696fefb1c31c24eb5736f1ef12a4de1", 252 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/test_runner/get_test_runner_code.ts": "2a4e26aa33120f3cc9e03b8538211a5047a4bad4c64e895944b87f2dcd55d904", 253 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/test_runner/test_runner.ts": "b91d77d9d4b82984cb2ba7431ba6935756ba72f62e7dd4db22cd47a680ebd952", 254 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/transform.deps.ts": "a6e138e380ebe4479bed1b00ae8dff1d4e6626cc53bc79dd98f907b43745d63a", 255 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/types.ts": "34e45a3136c2f21f797173ea46d9ea5d1639eb7b834a5bd565aad4214fa32603", 256 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/utils.ts": "d13b5b3148a2c71e9b2f1c84c7be7393b825ae972505e23c2f6b1e5287e96b43", 257 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/mod.ts": "691ea4b644cc61123b7beed19e66af301f25985483b81d21cfe49a0be2877fd9", 258 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/transform.ts": "1b127c5f22699c8ab2545b98aeca38c4e5c21405b0f5342ea17e9c46280ed277", 259 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/compiler.ts": "209ad2e1b294f93f87ec02ade9a0821f942d2e524104552d0aa8ff87021050a5", 260 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/compiler_transforms.ts": "cbb1fd5948f5ced1aa5c5aed9e45134e2357ce1e7220924c1d7bded30dcd0dd0", 261 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/mod.deps.ts": "30367fc68bcd2acf3b7020cf5cdd26f817f7ac9ac35c4bfb6c4551475f91bc3e", 262 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/npm_ignore.ts": "b430caa1905b65ae89b119d84857b3ccc3cb783a53fc083d1970e442f791721d", 263 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/package_json.ts": "61f35b06e374ed39ca776d29d67df4be7ee809d0bca29a8239687556c6d027c2", 264 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/pkg/dnt_wasm.generated.js": "65514d733c044bb394e4765321e33b73c490b20f86563293b5665d7a7b185153", 265 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be", 266 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/shims.ts": "df1bd4d9a196dca4b2d512b1564fff64ac6c945189a273d706391f87f210d7e6", 267 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/test_runner/get_test_runner_code.ts": "4dc7a73a13b027341c0688df2b29a4ef102f287c126f134c33f69f0339b46968", 268 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/test_runner/test_runner.ts": "4d0da0500ec427d5f390d9a8d42fb882fbeccc92c92d66b6f2e758606dbd40e6", 269 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/transform.deps.ts": "e42f2bdef46d098453bdba19261a67cf90b583f5d868f7fe83113c1380d9b85c", 270 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/types.ts": "b8e228b2fac44c2ae902fbb73b1689f6ab889915bd66486c8a85c0c24255f5fb", 271 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/lib/utils.ts": "878b7ac7003a10c16e6061aa49dbef9b42bd43174853ebffc9b67ea47eeb11d8", 272 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/mod.ts": "37d0c784371cf1750f30203a95de2555ba4c1aa89d826024f14c038f87e0f344", 273 | "https://raw.githubusercontent.com/denoland/dnt/0.37.0/transform.ts": "1b127c5f22699c8ab2545b98aeca38c4e5c21405b0f5342ea17e9c46280ed277" 274 | }, 275 | "npm": { 276 | "specifiers": { 277 | "@lit/reactive-element@1.6.2": "@lit/reactive-element@1.6.2", 278 | "compago@5.0.5": "compago@5.0.5", 279 | "esbuild@0.17.19": "esbuild@0.17.19", 280 | "linkedom": "linkedom@0.14.21", 281 | "linkedom@0.14.26": "linkedom@0.14.26", 282 | "lit": "lit@2.4.1", 283 | "lit@2.4.1": "lit@2.4.1", 284 | "lit@2.6.1": "lit@2.6.1", 285 | "lit@2.7.5": "lit@2.7.5" 286 | }, 287 | "packages": { 288 | "@esbuild/android-arm64@0.17.19": { 289 | "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", 290 | "dependencies": {} 291 | }, 292 | "@esbuild/android-arm@0.17.19": { 293 | "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", 294 | "dependencies": {} 295 | }, 296 | "@esbuild/android-x64@0.17.19": { 297 | "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", 298 | "dependencies": {} 299 | }, 300 | "@esbuild/darwin-arm64@0.17.19": { 301 | "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", 302 | "dependencies": {} 303 | }, 304 | "@esbuild/darwin-x64@0.17.19": { 305 | "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", 306 | "dependencies": {} 307 | }, 308 | "@esbuild/freebsd-arm64@0.17.19": { 309 | "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", 310 | "dependencies": {} 311 | }, 312 | "@esbuild/freebsd-x64@0.17.19": { 313 | "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", 314 | "dependencies": {} 315 | }, 316 | "@esbuild/linux-arm64@0.17.19": { 317 | "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", 318 | "dependencies": {} 319 | }, 320 | "@esbuild/linux-arm@0.17.19": { 321 | "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", 322 | "dependencies": {} 323 | }, 324 | "@esbuild/linux-ia32@0.17.19": { 325 | "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", 326 | "dependencies": {} 327 | }, 328 | "@esbuild/linux-loong64@0.17.19": { 329 | "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", 330 | "dependencies": {} 331 | }, 332 | "@esbuild/linux-mips64el@0.17.19": { 333 | "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", 334 | "dependencies": {} 335 | }, 336 | "@esbuild/linux-ppc64@0.17.19": { 337 | "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", 338 | "dependencies": {} 339 | }, 340 | "@esbuild/linux-riscv64@0.17.19": { 341 | "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", 342 | "dependencies": {} 343 | }, 344 | "@esbuild/linux-s390x@0.17.19": { 345 | "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", 346 | "dependencies": {} 347 | }, 348 | "@esbuild/linux-x64@0.17.19": { 349 | "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", 350 | "dependencies": {} 351 | }, 352 | "@esbuild/netbsd-x64@0.17.19": { 353 | "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", 354 | "dependencies": {} 355 | }, 356 | "@esbuild/openbsd-x64@0.17.19": { 357 | "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", 358 | "dependencies": {} 359 | }, 360 | "@esbuild/sunos-x64@0.17.19": { 361 | "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", 362 | "dependencies": {} 363 | }, 364 | "@esbuild/win32-arm64@0.17.19": { 365 | "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", 366 | "dependencies": {} 367 | }, 368 | "@esbuild/win32-ia32@0.17.19": { 369 | "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", 370 | "dependencies": {} 371 | }, 372 | "@esbuild/win32-x64@0.17.19": { 373 | "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", 374 | "dependencies": {} 375 | }, 376 | "@lit-labs/ssr-dom-shim@1.0.0": { 377 | "integrity": "sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==", 378 | "dependencies": {} 379 | }, 380 | "@lit-labs/ssr-dom-shim@1.1.1": { 381 | "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==", 382 | "dependencies": {} 383 | }, 384 | "@lit/reactive-element@1.4.2": { 385 | "integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w==", 386 | "dependencies": {} 387 | }, 388 | "@lit/reactive-element@1.6.1": { 389 | "integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", 390 | "dependencies": { 391 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.0.0" 392 | } 393 | }, 394 | "@lit/reactive-element@1.6.2": { 395 | "integrity": "sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==", 396 | "dependencies": { 397 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.1.1" 398 | } 399 | }, 400 | "@types/trusted-types@2.0.2": { 401 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", 402 | "dependencies": {} 403 | }, 404 | "boolbase@1.0.0": { 405 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", 406 | "dependencies": {} 407 | }, 408 | "compago@5.0.5": { 409 | "integrity": "sha512-AIsoUmoOmwDIviu0nFYx6Xqh2uTJFZrLk3OQiJfO0DTEvOCaimE4gfKSdw1p1ckv7njwjKBnIWpqGSuGpu2pqQ==", 410 | "dependencies": { 411 | "lit": "lit@2.7.5" 412 | } 413 | }, 414 | "css-select@5.1.0": { 415 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 416 | "dependencies": { 417 | "boolbase": "boolbase@1.0.0", 418 | "css-what": "css-what@6.1.0", 419 | "domhandler": "domhandler@5.0.3", 420 | "domutils": "domutils@3.0.1", 421 | "nth-check": "nth-check@2.1.1" 422 | } 423 | }, 424 | "css-what@6.1.0": { 425 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", 426 | "dependencies": {} 427 | }, 428 | "cssom@0.5.0": { 429 | "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", 430 | "dependencies": {} 431 | }, 432 | "dom-serializer@2.0.0": { 433 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 434 | "dependencies": { 435 | "domelementtype": "domelementtype@2.3.0", 436 | "domhandler": "domhandler@5.0.3", 437 | "entities": "entities@4.4.0" 438 | } 439 | }, 440 | "domelementtype@2.3.0": { 441 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", 442 | "dependencies": {} 443 | }, 444 | "domhandler@5.0.3": { 445 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 446 | "dependencies": { 447 | "domelementtype": "domelementtype@2.3.0" 448 | } 449 | }, 450 | "domutils@3.0.1": { 451 | "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", 452 | "dependencies": { 453 | "dom-serializer": "dom-serializer@2.0.0", 454 | "domelementtype": "domelementtype@2.3.0", 455 | "domhandler": "domhandler@5.0.3" 456 | } 457 | }, 458 | "entities@4.4.0": { 459 | "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", 460 | "dependencies": {} 461 | }, 462 | "esbuild@0.17.19": { 463 | "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", 464 | "dependencies": { 465 | "@esbuild/android-arm": "@esbuild/android-arm@0.17.19", 466 | "@esbuild/android-arm64": "@esbuild/android-arm64@0.17.19", 467 | "@esbuild/android-x64": "@esbuild/android-x64@0.17.19", 468 | "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.17.19", 469 | "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.17.19", 470 | "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.17.19", 471 | "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.17.19", 472 | "@esbuild/linux-arm": "@esbuild/linux-arm@0.17.19", 473 | "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.17.19", 474 | "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.17.19", 475 | "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.17.19", 476 | "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.17.19", 477 | "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.17.19", 478 | "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.17.19", 479 | "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.17.19", 480 | "@esbuild/linux-x64": "@esbuild/linux-x64@0.17.19", 481 | "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.17.19", 482 | "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.17.19", 483 | "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.17.19", 484 | "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.17.19", 485 | "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.17.19", 486 | "@esbuild/win32-x64": "@esbuild/win32-x64@0.17.19" 487 | } 488 | }, 489 | "html-escaper@3.0.3": { 490 | "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", 491 | "dependencies": {} 492 | }, 493 | "htmlparser2@8.0.1": { 494 | "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", 495 | "dependencies": { 496 | "domelementtype": "domelementtype@2.3.0", 497 | "domhandler": "domhandler@5.0.3", 498 | "domutils": "domutils@3.0.1", 499 | "entities": "entities@4.4.0" 500 | } 501 | }, 502 | "linkedom@0.14.21": { 503 | "integrity": "sha512-V+c0AAFMTVJA2iAhrdd+u44lL0TjL6hBenVB061VQ6BHqTAHtXw1v5F1/CHGKtwg0OHm+hrGbepb9ZSFJ7lJkg==", 504 | "dependencies": { 505 | "css-select": "css-select@5.1.0", 506 | "cssom": "cssom@0.5.0", 507 | "html-escaper": "html-escaper@3.0.3", 508 | "htmlparser2": "htmlparser2@8.0.1", 509 | "uhyphen": "uhyphen@0.1.0" 510 | } 511 | }, 512 | "linkedom@0.14.26": { 513 | "integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==", 514 | "dependencies": { 515 | "css-select": "css-select@5.1.0", 516 | "cssom": "cssom@0.5.0", 517 | "html-escaper": "html-escaper@3.0.3", 518 | "htmlparser2": "htmlparser2@8.0.1", 519 | "uhyphen": "uhyphen@0.2.0" 520 | } 521 | }, 522 | "lit-element@3.2.2": { 523 | "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", 524 | "dependencies": { 525 | "@lit/reactive-element": "@lit/reactive-element@1.4.2", 526 | "lit-html": "lit-html@2.4.0" 527 | } 528 | }, 529 | "lit-element@3.3.2": { 530 | "integrity": "sha512-xXAeVWKGr4/njq0rGC9dethMnYCq5hpKYrgQZYTzawt9YQhMiXfD+T1RgrdY3NamOxwq2aXlb0vOI6e29CKgVQ==", 531 | "dependencies": { 532 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.1.1", 533 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 534 | "lit-html": "lit-html@2.7.4" 535 | } 536 | }, 537 | "lit-html@2.4.0": { 538 | "integrity": "sha512-G6qXu4JNUpY6aaF2VMfaszhO9hlWw0hOTRFDmuMheg/nDYGB+2RztUSOyrzALAbr8Nh0Y7qjhYkReh3rPnplVg==", 539 | "dependencies": { 540 | "@types/trusted-types": "@types/trusted-types@2.0.2" 541 | } 542 | }, 543 | "lit-html@2.6.1": { 544 | "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", 545 | "dependencies": { 546 | "@types/trusted-types": "@types/trusted-types@2.0.2" 547 | } 548 | }, 549 | "lit-html@2.7.4": { 550 | "integrity": "sha512-/Jw+FBpeEN+z8X6PJva5n7+0MzCVAH2yypN99qHYYkq8bI+j7I39GH+68Z/MZD6rGKDK9RpzBw7CocfmHfq6+g==", 551 | "dependencies": { 552 | "@types/trusted-types": "@types/trusted-types@2.0.2" 553 | } 554 | }, 555 | "lit@2.4.1": { 556 | "integrity": "sha512-qohSgLiyN1cFnJG26dIiY03S4F49857A0AHQfnS0zYtnUVnD2MFvx+UT52rtXsIuNFQrnUupX+zyGSATlk1f/A==", 557 | "dependencies": { 558 | "@lit/reactive-element": "@lit/reactive-element@1.4.2", 559 | "lit-element": "lit-element@3.2.2", 560 | "lit-html": "lit-html@2.4.0" 561 | } 562 | }, 563 | "lit@2.6.1": { 564 | "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", 565 | "dependencies": { 566 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 567 | "lit-element": "lit-element@3.2.2", 568 | "lit-html": "lit-html@2.6.1" 569 | } 570 | }, 571 | "lit@2.7.5": { 572 | "integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==", 573 | "dependencies": { 574 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 575 | "lit-element": "lit-element@3.3.2", 576 | "lit-html": "lit-html@2.7.4" 577 | } 578 | }, 579 | "nth-check@2.1.1": { 580 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 581 | "dependencies": { 582 | "boolbase": "boolbase@1.0.0" 583 | } 584 | }, 585 | "uhyphen@0.1.0": { 586 | "integrity": "sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==", 587 | "dependencies": {} 588 | }, 589 | "uhyphen@0.2.0": { 590 | "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", 591 | "dependencies": {} 592 | } 593 | } 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { LitElement, ReactiveElement } from "npm:lit@2.7.5"; 2 | export type { PropertyDeclaration } from "npm:lit@2.7.5"; 3 | export { Directive, directive, PartType } from "npm:lit@2.7.5/directive.js"; 4 | export type { EventPart, PartInfo } from "npm:lit@2.7.5/directive.js"; 5 | -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zandaqo/compago/56901df379cf1fa738b0b26fca3fdbd4bd88a63b/examples/.DS_Store -------------------------------------------------------------------------------- /examples/counter/README.md: -------------------------------------------------------------------------------- 1 | # Counter 2 | 3 | A web component example using Deno, Lit, and Compago. 4 | 5 | ## Development 6 | 7 | The component is authored with Deno and requires no additional tooling for 8 | development. It uses the built-in Deno formatter, linter, test runner, and 9 | building tools to produce an NPM package for Node.js and browser. 10 | 11 | ### Formatting 12 | 13 | ```bash 14 | deno fmt 15 | ``` 16 | 17 | Deno's formatter (`dprint`) takes care of formatting most file types, just use 18 | `deno fmt` to format and `deno fmt --check` to check formatting. The formatter, 19 | however, doesn't yet support formatting html in tagged templates, so feel free 20 | to nag the Deno developers in the issue tracker: 21 | 22 | - [Format html tagged template text](https://github.com/dprint/dprint-plugin-typescript/issues/9) 23 | 24 | ### Linting 25 | 26 | ```bash 27 | deno lint 28 | ``` 29 | 30 | ### Testing 31 | 32 | ```bash 33 | deno task test 34 | ``` 35 | 36 | Deno's built-in test runner `deno test` is perfect for writing unit tests for 37 | our component. For a passable DOM implementation we can use `JSDOM`. You can 38 | check the working example in the 39 | [`couter_test.ts`](https://github.com/zandaqo/compago/blob/master/examples/counter/counter_test.ts) 40 | file. 41 | 42 | ### Building 43 | 44 | ```bash 45 | deno task build 46 | ``` 47 | 48 | While our resulting source code is readily available for consumption by Deno, we 49 | often need to use it with Node.js based toolchains and bundlers. Here we use a 50 | small build 51 | [script](https://github.com/zandaqo/compago/blob/master/examples/counter/_build.ts) 52 | that uses Deno's `dnt` to produce an NPM package that exports our component as a 53 | ESM module with peer dependencies on Lit and Compago. 54 | 55 | ## Known Issues 56 | 57 | 1. the formatter (`dprint`) doesn't yet support formatting HTML inside template 58 | strings: 59 | - [Format html tagged template text](https://github.com/dprint/dprint-plugin-typescript/issues/9) 60 | 2. We cannot use class fields for defining reactive properties since Deno 61 | doesn't support setting `useDefineForClassFields` to `false`, thus, class 62 | fields set on an instance overshadow reactive accessors set on prototype. 63 | - [Avoiding issues with class fields when declaring properties](https://lit.dev/docs/components/properties/#avoiding-issues-with-class-fields) 64 | - [Why is useDefineForClassFields non-overridable?](https://github.com/denoland/deno/issues/12393) 65 | -------------------------------------------------------------------------------- /examples/counter/_build.ts: -------------------------------------------------------------------------------- 1 | import { 2 | build, 3 | emptyDir, 4 | } from "https://raw.githubusercontent.com/denoland/dnt/0.37.0/mod.ts"; 5 | 6 | await emptyDir("npm"); 7 | 8 | await build({ 9 | entryPoints: ["./counter.ts"], 10 | outDir: "./npm", 11 | typeCheck: false, 12 | test: false, 13 | declaration: "inline", 14 | scriptModule: false, 15 | compilerOptions: { 16 | target: "Latest", 17 | sourceMap: true, 18 | inlineSources: true, 19 | }, 20 | shims: { 21 | deno: false, 22 | timers: false, 23 | }, 24 | package: { 25 | private: true, 26 | name: "compago-example-counter", 27 | version: "0.0.1", 28 | main: "counter.js", 29 | type: "module", 30 | description: "An example of a Web Component written with Deno", 31 | keywords: [ 32 | "web component", 33 | "deno", 34 | "lit", 35 | "compago", 36 | ], 37 | author: "Maga D. Zandaqo (http://maga.name)", 38 | license: "MIT", 39 | repository: { 40 | type: "git", 41 | url: "https://github.com/zandaqo/compago.git", 42 | }, 43 | homepage: "https://github.com/zandaqo/compago#readme", 44 | bugs: { 45 | url: "https://github.com/zandaqo/compago/issues", 46 | }, 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/counter/counter.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from "npm:lit@2.7.5"; 2 | import { state } from "npm:lit@2.7.5/decorators.js"; 3 | import { bond } from "npm:compago@5.0.5"; 4 | 5 | class MyCounter extends LitElement { 6 | // HACK: Have to use `declare` here and assign default value separately 7 | // in the constructor to avoid overshadowing accessors: 8 | // https://lit.dev/docs/components/properties/#avoiding-issues-with-class-fields 9 | @state() 10 | declare count: number; 11 | 12 | constructor() { 13 | super(); 14 | this.count = 0; 15 | } 16 | 17 | static styles = css` 18 | * { 19 | font-size: 200%; 20 | } 21 | 22 | span { 23 | width: 4rem; 24 | display: inline-block; 25 | text-align: center; 26 | } 27 | 28 | button { 29 | width: 64px; 30 | height: 64px; 31 | border: none; 32 | border-radius: 10px; 33 | background-color: seagreen; 34 | color: white; 35 | } 36 | `; 37 | 38 | render() { 39 | return html` 40 | 43 | ${this.count} 44 | 47 | `; 48 | } 49 | } 50 | 51 | declare global { 52 | interface HTMLElementTagNameMap { 53 | "my-counter": MyCounter; 54 | } 55 | } 56 | 57 | customElements.define("my-counter", MyCounter); 58 | -------------------------------------------------------------------------------- /examples/counter/counter_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.190.0/testing/asserts.ts"; 2 | import { JSDOM } from "https://esm.sh/jsdom@19.0.0"; 3 | 4 | const { 5 | window, 6 | } = new JSDOM( 7 | ``, 8 | { 9 | url: "https://example.com/", 10 | referrer: "https://example.org/", 11 | contentType: "text/html", 12 | }, 13 | ); 14 | globalThis.document = window.document; 15 | globalThis.HTMLElement = window.HTMLElement; 16 | globalThis.customElements = window.customElements; 17 | globalThis.HTMLIFrameElement = window.HTMLIFrameElement; 18 | globalThis.CSSStyleSheet = window.CSSStyleSheet; 19 | 20 | /* Load our Web Component asyncronously */ 21 | await import("./counter.ts"); 22 | 23 | Deno.test("Registers the component", () => { 24 | const element = document.createElement("my-counter"); 25 | assertEquals(element.count, 0); 26 | }); 27 | 28 | Deno.test("Changes the counter", async () => { 29 | const element = document.createElement("my-counter"); 30 | document.body.appendChild(element); 31 | await element.updateComplete; 32 | const [descrease, increase] = element.renderRoot.querySelectorAll("button"); 33 | increase.click(); 34 | assertEquals(element.count, 1); 35 | await element.updateComplete; 36 | increase.click(); 37 | assertEquals(element.count, 2); 38 | await element.updateComplete; 39 | descrease.click(); 40 | assertEquals(element.count, 1); 41 | }); 42 | -------------------------------------------------------------------------------- /examples/counter/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "dom.asynciterable", 8 | "deno.ns" 9 | ] 10 | }, 11 | "tasks": { 12 | "test": "deno test -A --no-check --location https://example.com", 13 | "build": "deno run -A --no-check _build.ts" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/counter/deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 5 | "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 6 | "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", 7 | "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", 8 | "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", 9 | "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", 10 | "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", 11 | "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", 12 | "https://deno.land/std@0.140.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", 13 | "https://deno.land/std@0.140.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", 14 | "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", 15 | "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", 16 | "https://deno.land/std@0.140.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", 17 | "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 18 | "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 19 | "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 20 | "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 21 | "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 22 | "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", 23 | "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 24 | "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 25 | "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 26 | "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", 27 | "https://deno.land/std@0.165.0/_util/asserts.ts": "d0844e9b62510f89ce1f9878b046f6a57bf88f208a10304aab50efcb48365272", 28 | "https://deno.land/std@0.165.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", 29 | "https://deno.land/std@0.165.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", 30 | "https://deno.land/std@0.165.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", 31 | "https://deno.land/std@0.165.0/async/debounce.ts": "dc8b92d4a4fe7eac32c924f2b8d3e62112530db70cadce27042689d82970b350", 32 | "https://deno.land/std@0.165.0/async/deferred.ts": "d8fb253ffde2a056e4889ef7e90f3928f28be9f9294b6505773d33f136aab4e6", 33 | "https://deno.land/std@0.165.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699", 34 | "https://deno.land/std@0.165.0/async/mod.ts": "dd0a8ed4f3984ffabe2fcca7c9f466b7932d57b1864ffee148a5d5388316db6b", 35 | "https://deno.land/std@0.165.0/async/mux_async_iterator.ts": "3447b28a2a582224a3d4d3596bccbba6e85040da3b97ed64012f7decce98d093", 36 | "https://deno.land/std@0.165.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239", 37 | "https://deno.land/std@0.165.0/async/tee.ts": "9af3a3e7612af75861308b52249e167f5ebc3dcfc8a1a4d45462d96606ee2b70", 38 | "https://deno.land/std@0.165.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4", 39 | "https://deno.land/std@0.165.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a", 40 | "https://deno.land/std@0.165.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179", 41 | "https://deno.land/std@0.165.0/collections/map_values.ts": "7e73685397409f2a1bc5356d89a58ce0249faf9e38db29434a8733144c877a2f", 42 | "https://deno.land/std@0.165.0/crypto/timing_safe_equal.ts": "3784958e40a5fe10429a68b75cc5f8d34356bf0bc2eb93c80c3033e2a6f17821", 43 | "https://deno.land/std@0.165.0/encoding/base64.ts": "c57868ca7fa2fbe919f57f88a623ad34e3d970d675bdc1ff3a9d02bba7409db2", 44 | "https://deno.land/std@0.165.0/encoding/base64url.ts": "a5f82a9fa703bd85a5eb8e7c1296bc6529e601ebd9642cc2b5eaa6b38fa9e05a", 45 | "https://deno.land/std@0.165.0/flags/mod.ts": "3b459daf581e047819b6b799c92b96c81b4eecbd5ea025643d54dc0abc6ac923", 46 | "https://deno.land/std@0.165.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", 47 | "https://deno.land/std@0.165.0/fmt/printf.ts": "111a4df40a81799da778421ae0260f278e5d79877e30e92403cd57d197a8d1b0", 48 | "https://deno.land/std@0.165.0/fs/eol.ts": "65b1e27320c3eec6fb653b27e20056ee3d015d3e91db388cfefa41616ebc7cb3", 49 | "https://deno.land/std@0.165.0/fs/exists.ts": "6a447912e49eb79cc640adacfbf4b0baf8e17ede6d5bed057062ce33c4fa0d68", 50 | "https://deno.land/std@0.165.0/http/http_status.ts": "897575a7d6bc2b9123f6a38ecbc0f03d95a532c5d92029315dc9f508e12526b8", 51 | "https://deno.land/std@0.165.0/io/buffer.ts": "245f1762a949082ddc0a6e9b15589d0be2d29c150266decd04320b8a8318f9f6", 52 | "https://deno.land/std@0.165.0/io/types.d.ts": "107e1e64834c5ba917c783f446b407d33432c5d612c4b3430df64fc2b4ecf091", 53 | "https://deno.land/std@0.165.0/node/_core.ts": "cd45c0cfa412ff57762b600710574a46f923dfa372003d9dfe52304000393b9b", 54 | "https://deno.land/std@0.165.0/node/_events.d.ts": "3899ee9c37055fbb750e32cb43d7c435077c04446af948300080e1a590c6edf0", 55 | "https://deno.land/std@0.165.0/node/_events.mjs": "303e8aa60ace559e4ca0d19e8475f87311bee9e8330b4b497644d70f2002fc27", 56 | "https://deno.land/std@0.165.0/node/_fs/_fs_access.ts": "5d81f8b3d3b0832f8444389158fa84d2126f423694d54772a8d1f19558973937", 57 | "https://deno.land/std@0.165.0/node/_fs/_fs_appendFile.ts": "f9d83bce0d3eae04246916da5b048313c24d88dfaf063f779c3434f0399d9042", 58 | "https://deno.land/std@0.165.0/node/_fs/_fs_chmod.ts": "c1a65080458afbeba8ad0824664f824b167c39ee02ef6b0d04bcbc123a3f897e", 59 | "https://deno.land/std@0.165.0/node/_fs/_fs_chown.ts": "e072f87628cfea956e945787f0e8af47be325b2e3183ae095279472b1fb9d085", 60 | "https://deno.land/std@0.165.0/node/_fs/_fs_close.ts": "b23915f763addce9d96116c1e3179d9d33c596903f57e20afa46c4333072a460", 61 | "https://deno.land/std@0.165.0/node/_fs/_fs_common.ts": "a4f820c9750fea15e90e32fab268658cc88113ce1b884a6cda43c85fbff5a3c7", 62 | "https://deno.land/std@0.165.0/node/_fs/_fs_constants.ts": "66a07e0c0279ec118851cf30570d25bce3ac13dedb269d53d04e342bbad28cca", 63 | "https://deno.land/std@0.165.0/node/_fs/_fs_copy.ts": "692b9235a9c267e4c2604b4a228da5ef1c0ad0977ed086187fc86bc978c82913", 64 | "https://deno.land/std@0.165.0/node/_fs/_fs_dir.ts": "beeafa572154696433d8b90424bba13a6139647877732f23d4ab4fc9608984ef", 65 | "https://deno.land/std@0.165.0/node/_fs/_fs_dirent.ts": "649c0a794e7b8d930cdd7e6a168b404aa0448bf784e0cfbe1bd6d25b99052273", 66 | "https://deno.land/std@0.165.0/node/_fs/_fs_exists.ts": "87b063b7b1a59b5d2302ba2de2204fbccc1bfbe7fafede8678694cae45b77682", 67 | "https://deno.land/std@0.165.0/node/_fs/_fs_fdatasync.ts": "bbd078fea6c62c64d898101d697aefbfbb722797a75e328a82c2a4f2e7eb963d", 68 | "https://deno.land/std@0.165.0/node/_fs/_fs_fstat.ts": "559ff6ff094337db37b0f3108aeaecf42672795af45b206adbd90105afebf9c6", 69 | "https://deno.land/std@0.165.0/node/_fs/_fs_fsync.ts": "590be69ce5363dd4f8867f244cfabe8df89d40f86bbbe44fd00d69411d0b798e", 70 | "https://deno.land/std@0.165.0/node/_fs/_fs_ftruncate.ts": "8eb2a9fcf026bd9b85dc07a22bc452c48db4be05ab83f5f2b6a0549e15c1f75f", 71 | "https://deno.land/std@0.165.0/node/_fs/_fs_futimes.ts": "89c71d38dd6b8e0a97470f3545d739ba04f6e3ca981a664404a825fd772ac2a2", 72 | "https://deno.land/std@0.165.0/node/_fs/_fs_link.ts": "f7c60f989a60becd6cdc1c553122be34f7c2ed83a900448757982683cebc0ffd", 73 | "https://deno.land/std@0.165.0/node/_fs/_fs_lstat.ts": "c26a406ccdbc95dd7dab75aca0019b45b41edc07bebd40d9de183780d647a064", 74 | "https://deno.land/std@0.165.0/node/_fs/_fs_mkdir.ts": "007e5cef536f1f64b10598951d8413aaebdc5ce6a26cb170b8cba1737a8c086d", 75 | "https://deno.land/std@0.165.0/node/_fs/_fs_mkdtemp.ts": "a037c457d6542eb16df903f7126a4ddb174e37c7d7a58c6fbff86a2147b78509", 76 | "https://deno.land/std@0.165.0/node/_fs/_fs_open.ts": "41bfe4259f679cf4b407b551896ff0debc16e9b8003be00dc93321a75031749f", 77 | "https://deno.land/std@0.165.0/node/_fs/_fs_opendir.ts": "4a678152c184b9bbdafc3fbf53f4b2a942f74b954bcbcf6104990957170b2d8b", 78 | "https://deno.land/std@0.165.0/node/_fs/_fs_read.ts": "6c9a63689bf373aa2b6572e077c446572074dd25868994b0804ac2c644ddf438", 79 | "https://deno.land/std@0.165.0/node/_fs/_fs_readFile.ts": "7c42f8cb4bad2e37a53314de7831c0735bae010712fd914471850caa4d322ffd", 80 | "https://deno.land/std@0.165.0/node/_fs/_fs_readdir.ts": "8d186b470aea8411c794687b20effaf1f478abb59956c67b671691d9444b7786", 81 | "https://deno.land/std@0.165.0/node/_fs/_fs_readlink.ts": "a5582656af6f09361ecb408ed3c0ad3cc3afd683403539e4c22aa06deab90fc0", 82 | "https://deno.land/std@0.165.0/node/_fs/_fs_realpath.ts": "0bf961c7b13d83e39b21255237b7ef352beb778a8274d03f2419907a8cd5c09c", 83 | "https://deno.land/std@0.165.0/node/_fs/_fs_rename.ts": "9aa3cf6643499a38ccbfb14435239c7fc0da6b4cb5b5ab1c9e676d42daf27b71", 84 | "https://deno.land/std@0.165.0/node/_fs/_fs_rm.ts": "82e926fe3e11e2a7f56116d3b7005372c339010cc1a7566a37a5591d19d680c6", 85 | "https://deno.land/std@0.165.0/node/_fs/_fs_rmdir.ts": "109fb603373cf5318f8d2e19b3704b8493b4de954873df28a0d066afd0b0f5e0", 86 | "https://deno.land/std@0.165.0/node/_fs/_fs_stat.ts": "4ccc93cd1938e5dcc5298feb9c6f0bc9f444fa8565c726854ea40210b93d254c", 87 | "https://deno.land/std@0.165.0/node/_fs/_fs_symlink.ts": "a9fe02e745a8ab28e152f37e316cb204382f86ebafc3bcf32a9374cf9d369181", 88 | "https://deno.land/std@0.165.0/node/_fs/_fs_truncate.ts": "1fe9cba3a54132426927639c024a7a354455e5a13b3b3143ad1c25ed0b5fc288", 89 | "https://deno.land/std@0.165.0/node/_fs/_fs_unlink.ts": "d845c8067a2ba55c443e04d2706e6a4e53735488b30fc317418c9f75127913b0", 90 | "https://deno.land/std@0.165.0/node/_fs/_fs_utimes.ts": "194eeb8dab1ebdf274f784a38241553cc440305461b30c987ecef1a24dfc01ca", 91 | "https://deno.land/std@0.165.0/node/_fs/_fs_watch.ts": "aef811e78e04cff3da30dcd334af8d85018f915d1ec7b95f05b2e4c48a7b7a4f", 92 | "https://deno.land/std@0.165.0/node/_fs/_fs_write.d.ts": "deb5c1a98b6cb1aa79f773f3b8fc9410463f0e30fede1ff9df2652fc11b69d35", 93 | "https://deno.land/std@0.165.0/node/_fs/_fs_write.mjs": "265f1291a2e908fd2da9fc3cb541be09a592119a29767708354a3bec18645b04", 94 | "https://deno.land/std@0.165.0/node/_fs/_fs_writeFile.ts": "7bd32ee95836bb04f71c63f3f055de147953f431d3afa5596915c22cf33e41ed", 95 | "https://deno.land/std@0.165.0/node/_fs/_fs_writev.d.ts": "2cc02fc9ed20292e09a22bf215635804f397d9b8ed2df62067a42f3be1b31b76", 96 | "https://deno.land/std@0.165.0/node/_fs/_fs_writev.mjs": "e500af8857779d404302658225c249b89f20fa4c40058c645b555dd70ca6b54f", 97 | "https://deno.land/std@0.165.0/node/_global.d.ts": "6dadaf8cec2a0c506b22170617286e0bdc80be53dd0673e67fc7dd37a1130c68", 98 | "https://deno.land/std@0.165.0/node/_http_agent.mjs": "a8b519a9a375a15d17c1c1fbd732576b24f7427a5ef75c58dbcb7242168b46f8", 99 | "https://deno.land/std@0.165.0/node/_http_common.ts": "73a9ec85e8d62a783b2a52577ebeafc3d69f9be21ba12e654dfbde455cf58162", 100 | "https://deno.land/std@0.165.0/node/_http_outgoing.ts": "dc400fca921c09bca194380a338eccc9559f7cc95cecf92a7427513eb280eb98", 101 | "https://deno.land/std@0.165.0/node/_next_tick.ts": "81c1826675493b76f90c646fb1274a4c84b5cc913a80ca4526c32cd7c46a0b06", 102 | "https://deno.land/std@0.165.0/node/_pako.mjs": "3a9980e5b5e8705de0672911e85dc74587b19a8bac95295eee45cd105db5569b", 103 | "https://deno.land/std@0.165.0/node/_process/exiting.ts": "bc9694769139ffc596f962087155a8bfef10101d03423b9dcbc51ce6e1f88fce", 104 | "https://deno.land/std@0.165.0/node/_process/process.ts": "d5bf113a4b62f4abe4cb7ec5a9d00d44dac0ad9e82a23d00cc27d71e05ae8b66", 105 | "https://deno.land/std@0.165.0/node/_process/stdio.mjs": "971c3b086040d8521562155db13f22f9971d5c42c852b2081d4d2f0d8b6ab6bd", 106 | "https://deno.land/std@0.165.0/node/_process/streams.mjs": "3ce63d9eb24a8a8ec45eeebf5c184b43d888064f663f87e8f453888368e00f90", 107 | "https://deno.land/std@0.165.0/node/_stream.d.ts": "83e9da2f81de3205926f1e86ba54442aa5a3caf4c5e84a4c8699402ad340142b", 108 | "https://deno.land/std@0.165.0/node/_stream.mjs": "9a80217d9734f6e4284aae0ea55dd82b243f5517bc1814e983ad41b01732f712", 109 | "https://deno.land/std@0.165.0/node/_tls_common.ts": "cba054e22530c7ba5bdc3f899bb9bcb0e14f844d1b46326528998ceb41e8ae71", 110 | "https://deno.land/std@0.165.0/node/_tls_wrap.ts": "2f631836edcc53023c808e0d0cb83321ee41c9ade1333edf2db03310cc06fce6", 111 | "https://deno.land/std@0.165.0/node/_util/_util_callbackify.ts": "a71353d5fde3dc785cfdf6b6bcad1379a9c78b374720af4aaa7f88ffab2bac0e", 112 | "https://deno.land/std@0.165.0/node/_utils.ts": "1085a229e910b3a6672f3c3be05507591811dc67be2ae2188e9fc9ef4376b172", 113 | "https://deno.land/std@0.165.0/node/_zlib.mjs": "8d765ea047fe24538bcee393873a75771c1ad70518dd338c6234aa1049a4daf0", 114 | "https://deno.land/std@0.165.0/node/_zlib_binding.mjs": "18f2c312e50b8d5b1585f9428c107ed07d62c9b78b96752962b6d5872d657f22", 115 | "https://deno.land/std@0.165.0/node/assert.ts": "25c4383a4aa6f953e1c04b8c6def66b910c040ffebff82fa24fc1f8f36ebd99b", 116 | "https://deno.land/std@0.165.0/node/assertion_error.ts": "438aca6fc88fca9ad2d0eaf9474efa4e8a42bc62d9df8591e44a2252f5d08962", 117 | "https://deno.land/std@0.165.0/node/async_hooks.ts": "d0b31c0ca74169782da1d804e4e1bfa344187f072c33c9b66b37721f0fba8f63", 118 | "https://deno.land/std@0.165.0/node/buffer.ts": "43f07b2d1523345bf35b7deb7fcdad6e916020a631a7bc1b5efcaff556db4e1d", 119 | "https://deno.land/std@0.165.0/node/child_process.ts": "2ce8176b7dbcb6c867869ba09201e9e715393d89b24ecc458c18f8cc59f07dda", 120 | "https://deno.land/std@0.165.0/node/dns.ts": "d2e6c33902cae784ce24d2e869eb87fa9a14cba0e2d1d4c197939fa4f0739803", 121 | "https://deno.land/std@0.165.0/node/events.ts": "f848398d3591534ca94ac6b852a9f3c4dbb2da310c3a26059cf4ff06b7eae088", 122 | "https://deno.land/std@0.165.0/node/fs.ts": "f948e8365914c122f8402e216ec10adf49bd60dd0da68e772f6fe083d4936186", 123 | "https://deno.land/std@0.165.0/node/http.ts": "b4bde4927fb6f92f6209a5268c91c06c656055d8941336cfac6c60a143c8d2cd", 124 | "https://deno.land/std@0.165.0/node/https.ts": "8a4fa81ff1785479b6bdbc638d57f8474328110e7b96dc29210319e81ed8f2d2", 125 | "https://deno.land/std@0.165.0/node/internal/assert.mjs": "118327c8866266534b30d3a36ad978204af7336dc2db3158b8167192918d4e06", 126 | "https://deno.land/std@0.165.0/node/internal/async_hooks.ts": "8eca5b80f58ffb259e9b3a73536dc2fe2e67d07fd24bfe2aee325a4aa435edb3", 127 | "https://deno.land/std@0.165.0/node/internal/buffer.d.ts": "90f674081428a61978b6d481c5f557ff743a3f4a85d7ae113caab48fdf5b8a63", 128 | "https://deno.land/std@0.165.0/node/internal/buffer.mjs": "70b74b34f1617b3492aee6dec35c7bdabbf26e034bfcf640aa61b3d63c5c850f", 129 | "https://deno.land/std@0.165.0/node/internal/child_process.ts": "924a69b4dcbac501a73ff25fe2d093e17f0fb054def6d95565e08d2cb5f0a98a", 130 | "https://deno.land/std@0.165.0/node/internal/crypto/_keys.ts": "63229ff3d8d15b5bd0a1d2ebc19313cbb8ac969875bf16df1ce4f2b497d74fb5", 131 | "https://deno.land/std@0.165.0/node/internal/crypto/constants.ts": "d2c8821977aef55e4d66414d623c24a2447791a8b49b6404b8db32d81e20c315", 132 | "https://deno.land/std@0.165.0/node/internal/dns/promises.ts": "acd0cb07f55b0be9b24ffba86060aab63c5b5eabee400ded28445b7e2bd8590d", 133 | "https://deno.land/std@0.165.0/node/internal/dns/utils.ts": "9de48245fa5dee62273c9aa4f0054f23abb023d5d46b165ae57198af2b92cf73", 134 | "https://deno.land/std@0.165.0/node/internal/dtrace.ts": "50dd0e77b0269e47ff673bdb9ad0ef0ea3a3c53ac30c1695883ce4748e04ca14", 135 | "https://deno.land/std@0.165.0/node/internal/error_codes.ts": "ac03c4eae33de3a69d6c98e8678003207eecf75a6900eb847e3fea3c8c9e6d8f", 136 | "https://deno.land/std@0.165.0/node/internal/errors.ts": "b9aec7d1fe3eaf21322d0ea9dc2fcb344055d6b0c7a1bd0f62a0c379a5baa799", 137 | "https://deno.land/std@0.165.0/node/internal/fixed_queue.ts": "455b3c484de48e810b13bdf95cd1658ecb1ba6bcb8b9315ffe994efcde3ba5f5", 138 | "https://deno.land/std@0.165.0/node/internal/fs/streams.ts": "e68430e65f42a8d8b793babae87dfaa406d61d3c54f985dcb0e3ee532eae8d4c", 139 | "https://deno.land/std@0.165.0/node/internal/fs/utils.mjs": "7b1bb3f46a676303d2a873bb9c36f199bd2c253451d4bd013ac906a6accea5bd", 140 | "https://deno.land/std@0.165.0/node/internal/hide_stack_frames.ts": "a91962ec84610bc7ec86022c4593cdf688156a5910c07b5bcd71994225c13a03", 141 | "https://deno.land/std@0.165.0/node/internal/http.ts": "deb5b3db113ea502cf01044d9f3e299e5faca2789f27f6a894947603b62c45ad", 142 | "https://deno.land/std@0.165.0/node/internal/idna.ts": "3aed89919e3078160733b6e6ac60fdb06052cf0418acbabcf86f90017d102b78", 143 | "https://deno.land/std@0.165.0/node/internal/net.ts": "1239886cd2508a68624c2dae8abf895e8aa3bb15a748955349f9ac5539032238", 144 | "https://deno.land/std@0.165.0/node/internal/normalize_encoding.mjs": "3779ec8a7adf5d963b0224f9b85d1bc974a2ec2db0e858396b5d3c2c92138a0a", 145 | "https://deno.land/std@0.165.0/node/internal/options.ts": "a23c285975e058cb26a19abcb048cd8b46ab12d21cfb028868ac8003fffb43ac", 146 | "https://deno.land/std@0.165.0/node/internal/primordials.mjs": "7cf5afe471583e4a384eeea109bdff276b6b7f3a3af830f99f951fb7d57ef423", 147 | "https://deno.land/std@0.165.0/node/internal/process/per_thread.mjs": "bc1be72a6a662bf81573c20fe74893374847a7302065ddf52fb3fb2af505f31f", 148 | "https://deno.land/std@0.165.0/node/internal/querystring.ts": "c3b23674a379f696e505606ddce9c6feabe9fc497b280c56705c340f4028fe74", 149 | "https://deno.land/std@0.165.0/node/internal/readline/callbacks.mjs": "17d9270a54fb5dceea8f894669e3401e5c6260bab075a1e9675a62f1fef50d8c", 150 | "https://deno.land/std@0.165.0/node/internal/readline/utils.mjs": "a93ebb99f85e0dbb4f05f4aff5583d12a15150e45c335e4ecf925e1879dc9c84", 151 | "https://deno.land/std@0.165.0/node/internal/stream_base_commons.ts": "07c8d367cf7d0f6f48a248d39fbe611af6dad02a33510fcc3ef23f9a9c898b41", 152 | "https://deno.land/std@0.165.0/node/internal/streams/destroy.mjs": "9c9bbeb172a437041d529829f433df72cf0b63ae49f3ee6080a55ffbef7572ad", 153 | "https://deno.land/std@0.165.0/node/internal/streams/end-of-stream.mjs": "38be76eaceac231dfde643e72bc0940625446bf6d1dbd995c91c5ba9fd59b338", 154 | "https://deno.land/std@0.165.0/node/internal/streams/state.mjs": "ec61f2fb46f58ab77b9dccd5ab10ad86753a26f858f7b4b7aaf85e9359c77557", 155 | "https://deno.land/std@0.165.0/node/internal/streams/utils.mjs": "a0a6b93a7e68ef52bef4ed00b0c82bb7e335abf360af57335b07c6a3fcdde717", 156 | "https://deno.land/std@0.165.0/node/internal/timers.mjs": "b43e24580cec2dd50f795e4342251a79515c0db21630c25b40fdc380a78b74e7", 157 | "https://deno.land/std@0.165.0/node/internal/url.ts": "eacef0ace4f4c5394e9818a81499f4871b2a993d1bd3b902047e44a381ef0e22", 158 | "https://deno.land/std@0.165.0/node/internal/util.mjs": "35d24fb775468cd24443bcf0ec68904b8aa44e5b53845491a5e3382421917f9a", 159 | "https://deno.land/std@0.165.0/node/internal/util/comparisons.ts": "4093f52f05d84842b46496e448fa8d708e25c6d6b8971505fd4913a9d7075934", 160 | "https://deno.land/std@0.165.0/node/internal/util/debuglog.ts": "570c399f0a066b81f0836eeb926b9142ae7f1738cee9abd85cd12ce32092d5af", 161 | "https://deno.land/std@0.165.0/node/internal/util/inspect.mjs": "1ddace0c97719d2cc0869ba177d375e96051301352ec235cbfb2ecbfcd4e8fba", 162 | "https://deno.land/std@0.165.0/node/internal/util/types.ts": "de6e2b7f9b9985ab881b1e78f05ae51d1fc829ae1584063df21e57b35312f3c4", 163 | "https://deno.land/std@0.165.0/node/internal/validators.mjs": "67deae0f488d013c8bf485742a5478112b8e48837cb2458c4a8b2669cf7017db", 164 | "https://deno.land/std@0.165.0/node/internal_binding/_libuv_winerror.ts": "801e05c2742ae6cd42a5f0fd555a255a7308a65732551e962e5345f55eedc519", 165 | "https://deno.land/std@0.165.0/node/internal_binding/_listen.ts": "c15a356ef4758770fc72d3ca4db33f0cc321016df1aafb927c027c0d73ac2c42", 166 | "https://deno.land/std@0.165.0/node/internal_binding/_node.ts": "e4075ba8a37aef4eb5b592c8e3807c39cb49ca8653faf8e01a43421938076c1b", 167 | "https://deno.land/std@0.165.0/node/internal_binding/_timingSafeEqual.ts": "80640f055101071cb3680a2d8a1fead5fd260ca8bf183efb94296b69463e06cd", 168 | "https://deno.land/std@0.165.0/node/internal_binding/_utils.ts": "1c50883b5751a9ea1b38951e62ed63bacfdc9d69ea665292edfa28e1b1c5bd94", 169 | "https://deno.land/std@0.165.0/node/internal_binding/_winerror.ts": "8811d4be66f918c165370b619259c1f35e8c3e458b8539db64c704fbde0a7cd2", 170 | "https://deno.land/std@0.165.0/node/internal_binding/ares.ts": "33ff8275bc11751219af8bd149ea221c442d7e8676e3e9f20ccb0e1f0aac61b8", 171 | "https://deno.land/std@0.165.0/node/internal_binding/async_wrap.ts": "b83e4021a4854b2e13720f96d21edc11f9905251c64c1bc625a361f574400959", 172 | "https://deno.land/std@0.165.0/node/internal_binding/buffer.ts": "781e1d13adc924864e6e37ecb5152e8a4e994cf394695136e451c47f00bda76c", 173 | "https://deno.land/std@0.165.0/node/internal_binding/cares_wrap.ts": "720e6d5cff7018bb3d00e1a49dd4c31f0fc6af3a593ab68cd39e3592ed163d61", 174 | "https://deno.land/std@0.165.0/node/internal_binding/config.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 175 | "https://deno.land/std@0.165.0/node/internal_binding/connection_wrap.ts": "9debd4210d29c658054476fcb640c900725f564ef35412c56dc79eb07213a7c1", 176 | "https://deno.land/std@0.165.0/node/internal_binding/constants.ts": "f4afc504137fb21f3908ab549931604968dfa62432b285a0874f41c4cade9ed2", 177 | "https://deno.land/std@0.165.0/node/internal_binding/contextify.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 178 | "https://deno.land/std@0.165.0/node/internal_binding/credentials.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 179 | "https://deno.land/std@0.165.0/node/internal_binding/crypto.ts": "d7f39700dc020364edf7f4785e5026bb91f099ce1bd02734182451b1af300c8c", 180 | "https://deno.land/std@0.165.0/node/internal_binding/errors.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 181 | "https://deno.land/std@0.165.0/node/internal_binding/fs.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 182 | "https://deno.land/std@0.165.0/node/internal_binding/fs_dir.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 183 | "https://deno.land/std@0.165.0/node/internal_binding/fs_event_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 184 | "https://deno.land/std@0.165.0/node/internal_binding/handle_wrap.ts": "71c451060c9f555066d3ebe80de039a4e493a94b76c664450fbefd8f4167eb7e", 185 | "https://deno.land/std@0.165.0/node/internal_binding/heap_utils.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 186 | "https://deno.land/std@0.165.0/node/internal_binding/http_parser.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 187 | "https://deno.land/std@0.165.0/node/internal_binding/icu.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 188 | "https://deno.land/std@0.165.0/node/internal_binding/inspector.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 189 | "https://deno.land/std@0.165.0/node/internal_binding/js_stream.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 190 | "https://deno.land/std@0.165.0/node/internal_binding/messaging.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 191 | "https://deno.land/std@0.165.0/node/internal_binding/mod.ts": "f68e74e8eed84eaa6b0de24f0f4c47735ed46866d7ee1c5a5e7c0667b4f0540f", 192 | "https://deno.land/std@0.165.0/node/internal_binding/module_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 193 | "https://deno.land/std@0.165.0/node/internal_binding/native_module.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 194 | "https://deno.land/std@0.165.0/node/internal_binding/natives.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 195 | "https://deno.land/std@0.165.0/node/internal_binding/node_file.ts": "37d6864897547d95ca24e0f5d01035915db0065bff128bc22191bc93f9ad59ad", 196 | "https://deno.land/std@0.165.0/node/internal_binding/node_options.ts": "b098e6a1c80fa5003a1669c6828539167ab19337e13d20a47b610144cb888cef", 197 | "https://deno.land/std@0.165.0/node/internal_binding/options.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 198 | "https://deno.land/std@0.165.0/node/internal_binding/os.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 199 | "https://deno.land/std@0.165.0/node/internal_binding/performance.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 200 | "https://deno.land/std@0.165.0/node/internal_binding/pipe_wrap.ts": "105c73f268cb9ca1c6cebaf4bea089ab12e0c21c8c4e10bb0a14b0abd3e1661e", 201 | "https://deno.land/std@0.165.0/node/internal_binding/process_methods.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 202 | "https://deno.land/std@0.165.0/node/internal_binding/report.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 203 | "https://deno.land/std@0.165.0/node/internal_binding/serdes.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 204 | "https://deno.land/std@0.165.0/node/internal_binding/signal_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 205 | "https://deno.land/std@0.165.0/node/internal_binding/spawn_sync.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 206 | "https://deno.land/std@0.165.0/node/internal_binding/stream_wrap.ts": "ecbd50a6c6ff7f6fea9bdfdc7b3977637cd854814c812b59296458ca2f0fc209", 207 | "https://deno.land/std@0.165.0/node/internal_binding/string_decoder.ts": "5cb1863763d1e9b458bc21d6f976f16d9c18b3b3f57eaf0ade120aee38fba227", 208 | "https://deno.land/std@0.165.0/node/internal_binding/symbols.ts": "51cfca9bb6132d42071d4e9e6b68a340a7f274041cfcba3ad02900886e972a6c", 209 | "https://deno.land/std@0.165.0/node/internal_binding/task_queue.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 210 | "https://deno.land/std@0.165.0/node/internal_binding/tcp_wrap.ts": "4217fa10072e048a26f26e5f548b3f38422452c9956265592cac57379a610acb", 211 | "https://deno.land/std@0.165.0/node/internal_binding/timers.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 212 | "https://deno.land/std@0.165.0/node/internal_binding/tls_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 213 | "https://deno.land/std@0.165.0/node/internal_binding/trace_events.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 214 | "https://deno.land/std@0.165.0/node/internal_binding/tty_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 215 | "https://deno.land/std@0.165.0/node/internal_binding/types.ts": "4c26fb74ba2e45de553c15014c916df6789529a93171e450d5afb016b4c765e7", 216 | "https://deno.land/std@0.165.0/node/internal_binding/udp_wrap.ts": "cdd0882eff7e7db631d808608d20f5d1269b40fbcedd4a0972d6ed616a855c79", 217 | "https://deno.land/std@0.165.0/node/internal_binding/url.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 218 | "https://deno.land/std@0.165.0/node/internal_binding/util.ts": "faf5146c3cc3b2d6c26026a818b4a16e91488ab26e63c069f36ba3c3ae24c97b", 219 | "https://deno.land/std@0.165.0/node/internal_binding/uv.ts": "3a63eebe0b69334bab5e9f037aadde1aef59ab21ab0e4cd01ac588d919be2c75", 220 | "https://deno.land/std@0.165.0/node/internal_binding/v8.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 221 | "https://deno.land/std@0.165.0/node/internal_binding/worker.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 222 | "https://deno.land/std@0.165.0/node/internal_binding/zlib.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4", 223 | "https://deno.land/std@0.165.0/node/net.ts": "b13b3dac2ae3fe8b428610fbfdc6ce14f21862140005de4da40ac074bca13ad8", 224 | "https://deno.land/std@0.165.0/node/os.ts": "96b191f0de80cdc914aa3a78de3827a8658387b1b17ccccc6ed58db3e029ec76", 225 | "https://deno.land/std@0.165.0/node/path.ts": "c65858e9cbb52dbc0dd348eefcdc41e82906c39cfa7982f2d4d805e828414b8c", 226 | "https://deno.land/std@0.165.0/node/path/_constants.ts": "591787ca44a55859644a2d5dbaef43698ab29e72e58fd498ea5e8f78a341ba20", 227 | "https://deno.land/std@0.165.0/node/path/_interface.ts": "4a6e96c17c1b5acb2b5909583d5808fb4e80e5120d230dd028ec04d2e7d0d906", 228 | "https://deno.land/std@0.165.0/node/path/_util.ts": "70b4b58098c4638f3bf719fa700c95e308e3984a3f9aca551fab713426ba3cbe", 229 | "https://deno.land/std@0.165.0/node/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 230 | "https://deno.land/std@0.165.0/node/path/glob.ts": "ea65cb4a531e39173d4b93881ef018d27187be6153386fb8fdb5534838726c04", 231 | "https://deno.land/std@0.165.0/node/path/mod.ts": "f9125e20031aac43eef8baa58d852427c762541574513f6870d1d0abd8103252", 232 | "https://deno.land/std@0.165.0/node/path/posix.ts": "e40c13ad352b3814116d25fed5196b59445e98c7802f9b55c0a8e33f0ddfb5bd", 233 | "https://deno.land/std@0.165.0/node/path/separator.ts": "7176165f5b8351306357503357a3b241861d8b4e6c60aeb4e992da94f9487dc2", 234 | "https://deno.land/std@0.165.0/node/path/win32.ts": "6554159c6b21a73bb4647c85c44f42cdbbd84c53c37f57a3a195a47d50c0ab92", 235 | "https://deno.land/std@0.165.0/node/process.ts": "7180a9e7022d62481daf6fac855f440882137046ef6941f659e8d40c077186be", 236 | "https://deno.land/std@0.165.0/node/punycode.ts": "11b5821448d9e815058a18e5a86ea7abfacc55dca9d444ff27af81069068e2e3", 237 | "https://deno.land/std@0.165.0/node/querystring.ts": "ec6d8bd8b138a5c53fe8bc25a37bdd654d5fb76918fb6543864d62474b3860a8", 238 | "https://deno.land/std@0.165.0/node/stream.ts": "2c6d5d207d0ad295f396b34fd03a908c1638beb1754bc9c1fccd9a4cdcace8be", 239 | "https://deno.land/std@0.165.0/node/string_decoder.ts": "39b3846318f3a60cee417673dab0252a6f400af2e0f2a481bb6d74c00524b597", 240 | "https://deno.land/std@0.165.0/node/timers.ts": "ce7574023960d85c6d884d4f5b109a4b96bb58435cb97b46fe047714f4fafc7b", 241 | "https://deno.land/std@0.165.0/node/tls.ts": "68b9f801654f04664215c2f682d2ce32271c9e2a770cd91285ee864c6c56e0eb", 242 | "https://deno.land/std@0.165.0/node/url.ts": "79e7f78678301df8cf15c5f0d4446a7e4754a9e641a99da41b8c0568587e9b84", 243 | "https://deno.land/std@0.165.0/node/util.ts": "e926d996318017b41812983cacde63e39c92385de7a19a0cb788fc68f73318db", 244 | "https://deno.land/std@0.165.0/node/util/types.ts": "5948b43e834f73a4becf85b02049632560c65da9c1127e5c533c83d200d3dfcd", 245 | "https://deno.land/std@0.165.0/node/vm.ts": "4043eafffa3fff0668d3a59162e9b0fbd4b5b43a793ba553fe787a2cae6e149b", 246 | "https://deno.land/std@0.165.0/node/zlib.ts": "048c59e4c928be22146f3418c6a2fdb5417eb1e756213659c2a92ab5a865d2d1", 247 | "https://deno.land/std@0.165.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 248 | "https://deno.land/std@0.165.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 249 | "https://deno.land/std@0.165.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", 250 | "https://deno.land/std@0.165.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 251 | "https://deno.land/std@0.165.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 252 | "https://deno.land/std@0.165.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", 253 | "https://deno.land/std@0.165.0/path/posix.ts": "6b63de7097e68c8663c84ccedc0fd977656eb134432d818ecd3a4e122638ac24", 254 | "https://deno.land/std@0.165.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 255 | "https://deno.land/std@0.165.0/path/win32.ts": "7cebd2bda6657371adc00061a1d23fdd87bcdf64b4843bb148b0b24c11b40f69", 256 | "https://deno.land/std@0.165.0/streams/conversion.ts": "555c6c249f3acf85655f2d0af52d1cb3168e40b1c1fa26beefea501b333abe28", 257 | "https://deno.land/std@0.165.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", 258 | "https://deno.land/std@0.165.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", 259 | "https://deno.land/std@0.165.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8", 260 | "https://deno.land/std@0.171.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 261 | "https://deno.land/std@0.171.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 262 | "https://deno.land/std@0.171.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", 263 | "https://deno.land/std@0.171.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 264 | "https://deno.land/std@0.171.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 265 | "https://deno.land/std@0.171.0/fs/expand_glob.ts": "536055845aafc32de7e7a46c3b778a741825d5e2ed8580d9851a01ec7a5adf2e", 266 | "https://deno.land/std@0.171.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 267 | "https://deno.land/std@0.171.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 268 | "https://deno.land/std@0.171.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 269 | "https://deno.land/std@0.171.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8", 270 | "https://deno.land/std@0.171.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 271 | "https://deno.land/std@0.171.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 272 | "https://deno.land/std@0.171.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232", 273 | "https://deno.land/std@0.171.0/path/posix.ts": "2ecc259e6f34013889b7638ff90339a82d8178f629155761ce6001e41af55a43", 274 | "https://deno.land/std@0.171.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 275 | "https://deno.land/std@0.171.0/path/win32.ts": "99170a0eb0e2b1ce41499c1e86bb55320cb6606299ad947f57ee0a291cdb93d5", 276 | "https://deno.land/std@0.190.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 277 | "https://deno.land/std@0.190.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 278 | "https://deno.land/std@0.190.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 279 | "https://deno.land/std@0.190.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f", 280 | "https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", 281 | "https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", 282 | "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", 283 | "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", 284 | "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", 285 | "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", 286 | "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", 287 | "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", 288 | "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", 289 | "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", 290 | "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", 291 | "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", 292 | "https://deno.land/x/deno_graph@0.26.0/lib/deno_graph.generated.js": "2f7ca85b2ceb80ec4b3d1b7f3a504956083258610c7b9a1246238c5b7c68f62d", 293 | "https://deno.land/x/deno_graph@0.26.0/lib/loader.ts": "380e37e71d0649eb50176a9786795988fc3c47063a520a54b616d7727b0f8629", 294 | "https://deno.land/x/deno_graph@0.26.0/lib/media_type.ts": "222626d524fa2f9ebcc0ec7c7a7d5dfc74cc401cc46790f7c5e0eab0b0787707", 295 | "https://deno.land/x/deno_graph@0.26.0/lib/snippets/deno_graph-de651bc9c240ed8d/src/deno_apis.js": "41192baaa550a5c6a146280fae358cede917ae16ec4e4315be51bef6631ca892", 296 | "https://deno.land/x/deno_graph@0.26.0/lib/types.d.ts": "2bbdbf895321d1df8db511fab00160a0211c09c2e7cac56c522dd6e9ed6d2ef7", 297 | "https://deno.land/x/deno_graph@0.26.0/mod.ts": "11131ae166580a1c7fa8506ff553751465a81c263d94443f18f353d0c320bc14", 298 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", 299 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.d.ts": "607e651c5ae5aa57c2ac4090759a6379e809c0cdc42114742ac67353b1a75038", 300 | "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.js": "91a954daa993c5acb3361aa5279394f81ea6fe18b3854345c86111b336491cfc", 301 | "https://deno.land/x/ts_morph@17.0.1/common/DenoRuntime.ts": "537800e840d0994f9055164e11bf33eadf96419246af0d3c453793c3ae67bdb3", 302 | "https://deno.land/x/ts_morph@17.0.1/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", 303 | "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.d.ts": "ee7767b0c68b23c65bb607c94b6cb3512e8237fbcb7d1d8383a33235cde2c068", 304 | "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.js": "49a79124b941ba2b35d81ac9eb90fc33c957b2640cdb97569c1941bac5a3bbdb", 305 | "https://deno.land/x/ts_morph@17.0.1/common/typescript.d.ts": "57e52a0882af4e835473dda27e4316cc31149866970210f9f79b940e916b7838", 306 | "https://deno.land/x/ts_morph@17.0.1/common/typescript.js": "5dd669eb199ee2a539924c63a92e23d95df43dfe2fbe3a9d68c871648be1ad5e", 307 | "https://esm.sh/jsdom@19.0.0": "355c2ecce294290c939fcf71fd699661800b38f188a99860ddac67ada5eaf50c", 308 | "https://esm.sh/v99/@tootallnate/once@2.0.0/deno/once.js": "7519d1791fba77fa4f8f94e8473df63efd82057da71e44ada2f1f489917ba1cf", 309 | "https://esm.sh/v99/@types/jsdom@20.0.1/base.d.ts": "21a6219cd98fde0037921524ca0b20c1973475c37226d81640fd7d3a47ef80ee", 310 | "https://esm.sh/v99/@types/jsdom@20.0.1/index.d.ts": "9150ca9281851a6bb54223a06dff451e1bd3de4108d6d20ece49bc6e9bf7ecce", 311 | "https://esm.sh/v99/@types/node@16.18.3/events.d.ts": "5e3eb598ad8076bcf47be44ff0b955ac2a2bfbdcf2ea5fb4ef4f884ba2ee7fbe", 312 | "https://esm.sh/v99/@types/node@16.18.3/vm.d.ts": "280af338f3557d60ceb42401328ef5fdd07f83413baeb774117b462767a88589", 313 | "https://esm.sh/v99/@types/tough-cookie@4.0.2/index.d.ts": "cc256fd958b33576ed32c7338c64adb0d08fc0c2c6525010202fab83f32745da", 314 | "https://esm.sh/v99/abab@2.0.6/deno/abab.js": "e20a7b2d461559a887affac75add68611dcea069c0bfab2e0ec48150127e64d6", 315 | "https://esm.sh/v99/agent-base@6.0.2/deno/agent-base.js": "50260f8cc79edb93c1828db5bdce71c18cc82eee4ff626b22655c2990c2cad82", 316 | "https://esm.sh/v99/browser-process-hrtime@1.0.0/deno/browser-process-hrtime.js": "672be9f4610f52c1471b1189a2b2719c67d332d62194a8f76b0903a0adebbb05", 317 | "https://esm.sh/v99/canvas@2.10.2/deno/canvas.js": "88074f400f70e0232b40c1a9ea9ad5f4e69a141a6142882240489f99f07f892a", 318 | "https://esm.sh/v99/cssom@0.3.8/deno/cssom.js": "7ea1e35bbed3f1930b8d5912aaf279985e199778e5c07555d4a9419dcaf5f9fc", 319 | "https://esm.sh/v99/cssom@0.5.0/deno/cssom.js": "7e95364494c05d636ba66d7a2bfaddfa9a2b0de1b53e9dc54ff7073451bba060", 320 | "https://esm.sh/v99/cssstyle@2.3.0/deno/cssstyle.js": "35e2dda93fadb3b31a966d4dbe2c2fc6509e307f9f7df52037305f18dee0db97", 321 | "https://esm.sh/v99/data-urls@3.0.2/deno/data-urls.js": "d50fcbbbf63475a0eb64e25e217e8bf0158fba384e52900f978109f12de7e443", 322 | "https://esm.sh/v99/debug@4.3.4/deno/debug.js": "81975ccfa450b354e05b55f5b84dfa1b3c19a625d7e14c3555534822427c89e5", 323 | "https://esm.sh/v99/decimal.js@10.4.2/deno/decimal.js": "7dedfbe3fad1a0c2a1688de8d71c806beb43788c414a4ce0655dda0309b8d590", 324 | "https://esm.sh/v99/domexception@4.0.0/deno/lib/DOMException-impl.js": "adba2edbc86d985c0551f9e88d9a6dd384b2e25dfd53cae959ccdc723eaf9140", 325 | "https://esm.sh/v99/domexception@4.0.0/deno/lib/DOMException.js": "8e6b5d9885b1d67109aaaffe6608a4dfc9c25e8063c67e8f67c805eaffe34d5b", 326 | "https://esm.sh/v99/domexception@4.0.0/deno/lib/legacy-error-codes.json.js": "09d8056989da3dfc74acbab58a72fb9455ed61dbb7f418e0fa934d72818e454e", 327 | "https://esm.sh/v99/domexception@4.0.0/deno/lib/utils.js": "e30725418edcf605b7a0ddcf42576c88863ca8a48640717dc976e428f7b96520", 328 | "https://esm.sh/v99/domexception@4.0.0/deno/webidl2js-wrapper.js": "96618b3700e9a0826df647190ae4815103693996db528df9c7549674a5a6ab47", 329 | "https://esm.sh/v99/form-data@4.0.0/deno/form-data.js": "6632afac7b0debca7e89338401e28664b4391889a23c9cb44bbafd37f9a84515", 330 | "https://esm.sh/v99/html-encoding-sniffer@3.0.0/deno/html-encoding-sniffer.js": "927c797519e7b6791f5c35a91ff5ab26a9454d49c7381e2bb7fb90176ce1d1f3", 331 | "https://esm.sh/v99/http-proxy-agent@5.0.0/deno/http-proxy-agent.js": "dbeb32dcc343be4b5c20cde874ba0db2be9fd2905509086e81b066cdd0ce9447", 332 | "https://esm.sh/v99/https-proxy-agent@5.0.1/deno/https-proxy-agent.js": "51a7ca09c29e36b9e16be9461f97608d2f6911ed3d0fe56063e4618e68b23aae", 333 | "https://esm.sh/v99/iconv-lite@0.6.3/deno/iconv-lite.js": "79b5e3fecac5f21a7695cbc2e54953ed638f4e7db486323627a408c72ff1c220", 334 | "https://esm.sh/v99/is-potential-custom-element-name@1.0.1/deno/is-potential-custom-element-name.js": "924765b99770a50fdca32f5bd568cbb4ba0f8ee2c69a2291a2eaa230b04c5fb7", 335 | "https://esm.sh/v99/jsdom@19.0.0/deno/jsdom.js": "797a8c1c0bd77cddfce4e34ee90c0cf402cbdde0a2d779f073dc634133db9059", 336 | "https://esm.sh/v99/ms@2.1.2/deno/ms.js": "ab75cfcc21a76d590b1ff32ac69dd38efbe23610a4e4f71eabf543766c68ad5d", 337 | "https://esm.sh/v99/node.ns.d.ts": "0fb081f0cd2150931bd54baa04f31466abd9ca701fd9060d07931402cf8367ba", 338 | "https://esm.sh/v99/node_buffer.js": "445d389cb8c9f9fad5ff869ca2c2a863d178d410795ebd5f2f39d9a029156d56", 339 | "https://esm.sh/v99/nwsapi@2.2.2/deno/nwsapi.js": "fea3c2ee1e035b045c34173d9f12833ce2bf34c9e84bf6bc0c2898d73d627efe", 340 | "https://esm.sh/v99/parse5@6.0.1/deno/lib/common/html.js": "9bd40c29fc2f4eb073713f458055116480b79c5e3fcbf15c93208837b08499f3", 341 | "https://esm.sh/v99/parse5@6.0.1/deno/lib/parser/open-element-stack.js": "d81a7c1b8cddc1d6026f9e9e0e4c77dfefca40b63c65662daca04f8416c7a447", 342 | "https://esm.sh/v99/parse5@6.0.1/deno/parse5.js": "3d7a6b84fb2ce1c49dd11cf05a9a261ba0f21ebe7a8d863b72daca9c8c2214a4", 343 | "https://esm.sh/v99/parse5@7.1.1/dist/common/error-codes.d.ts": "7df4fd1dc84c283ac82df2670ef10db5ead9206c8fca1985f6c585e7f03431a4", 344 | "https://esm.sh/v99/parse5@7.1.1/dist/common/foreign-content.d.ts": "c5404e44d82481de13e5475e8ca5d078f3ab9c6db79402304807e19954d48e0a", 345 | "https://esm.sh/v99/parse5@7.1.1/dist/common/html.d.ts": "079b83d3586b16307e086a71c76121be15c233505bd8b58873b394da8d1bf2e8", 346 | "https://esm.sh/v99/parse5@7.1.1/dist/common/token.d.ts": "3d8198a77ce523b86e02ecedda42033139974bd76ee3bae4bead6061d77e13d6", 347 | "https://esm.sh/v99/parse5@7.1.1/dist/index.d.ts": "31ce53d7b6134df12587c1f0c1562876a2976cb6d6564f5937d10352115745bc", 348 | "https://esm.sh/v99/parse5@7.1.1/dist/parser/formatting-element-list.d.ts": "18ad664318910752f33bc22b95bfeae88762c656ab53213b3294edc4d0f17d4f", 349 | "https://esm.sh/v99/parse5@7.1.1/dist/parser/index.d.ts": "1440bb339cf31fd19548ee1a4cf76c5407755fa18dce0407775f1116b1af772f", 350 | "https://esm.sh/v99/parse5@7.1.1/dist/parser/open-element-stack.d.ts": "45c0c45628ac836809326c6bb3a460b0ed2badb9d048010cdbeabd165c40225f", 351 | "https://esm.sh/v99/parse5@7.1.1/dist/serializer/index.d.ts": "0628ab347e1b089de8de885418d7f2080bd40f5adacd9e4b7acd59fa8a23e24e", 352 | "https://esm.sh/v99/parse5@7.1.1/dist/tokenizer/index.d.ts": "f00e2225d74db630612d259ffbe641b475b984451abad6b288084ce15f329430", 353 | "https://esm.sh/v99/parse5@7.1.1/dist/tokenizer/preprocessor.d.ts": "f10ad5c2b19619111428aca33cb4b1d19eb3bc0f0030ccb0d91461c421c9bdab", 354 | "https://esm.sh/v99/parse5@7.1.1/dist/tree-adapters/default.d.ts": "3b5d7caa96f38aa267005569546cf5abc3330d71a6b7ac692c7d6fb1266655b0", 355 | "https://esm.sh/v99/parse5@7.1.1/dist/tree-adapters/interface.d.ts": "6ee43b3ef5aa656e49339a3fbdad56806ff5117b0bda58c5e84e0570ccad0bcc", 356 | "https://esm.sh/v99/psl@1.9.0/deno/psl.js": "7663513b21192268b51192e6840d6c5f4f671511c65bc7a1c006127e54fe4933", 357 | "https://esm.sh/v99/querystringify@2.2.0/deno/querystringify.js": "4a21f6f9822f078d649802d0dc42760eadaa9d54c15f0c3f100378b7386d9268", 358 | "https://esm.sh/v99/requires-port@1.0.0/deno/requires-port.js": "0f8d6db4473dcee9e2f27d2248e5332e418882dfb2a929200541d1cc27ef897c", 359 | "https://esm.sh/v99/safer-buffer@2.1.2/deno/safer-buffer.js": "70ff19ed6c857ed344fd1302e4ab6ab5fe745f47dbfdea1cb17381b57883d15d", 360 | "https://esm.sh/v99/saxes@5.0.1/deno/saxes.js": "9e886a69a560086fe3bfe1ef81ee308a0054bbe1d6753a358c7027e11c61e4a0", 361 | "https://esm.sh/v99/symbol-tree@3.2.4/deno/symbol-tree.js": "6f225e2358e43486bbcce73d95ad2aaaeb7a29985668bd8508e47e765a14ea23", 362 | "https://esm.sh/v99/tough-cookie@4.1.2/deno/tough-cookie.js": "2851d64ca3523ce40898c400a446301ef3b3590f1fbfe0bb2f9caf5a18609a51", 363 | "https://esm.sh/v99/tr46@3.0.0/deno/tr46.js": "97a6f8a5d5023fdcdb91935697c37e0e4c03a6f5f924082a2d4c14b67ec24473", 364 | "https://esm.sh/v99/universalify@0.2.0/deno/universalify.js": "69f7ec014c7623aa1a89395ad535fc15fbe2ad47499ae756b5ef3f3937ab3bcb", 365 | "https://esm.sh/v99/url-parse@1.5.10/deno/url-parse.js": "1e0003d66a1fd87364d44a6ee6eae9a32d846da62b08348e765e240275a61bd8", 366 | "https://esm.sh/v99/w3c-hr-time@1.0.2/deno/w3c-hr-time.js": "267dd7f74fb6f3bc4db0eda3962692a4f88d4a2097c9a99800bc6a125e5a355b", 367 | "https://esm.sh/v99/w3c-xmlserializer@3.0.0/deno/w3c-xmlserializer.js": "22b4a488abee15bd2afc0d7b08e4012d2b9ac3919f8d29050765ea24b9721c3e", 368 | "https://esm.sh/v99/webidl-conversions@7.0.0/deno/webidl-conversions.js": "b73b16a0649b90b81cad4046cc13ced8bc85f8eff48a87d69e0ea9ae503e49b8", 369 | "https://esm.sh/v99/whatwg-encoding@2.0.0/deno/whatwg-encoding.js": "5578306c060c5ecb666d2793ef0407d89306c69885627105d23903b4470d7d37", 370 | "https://esm.sh/v99/whatwg-mimetype@3.0.0/deno/whatwg-mimetype.js": "2634c9f1a38fc20d7430a37f2a65cbf80d40310279b53afb8ff4714b54012d8f", 371 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/Function.js": "2ec4992562c98d2af4172f950bf9b8d3fac2aa03d63e0d262de6078064ac1273", 372 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/URL-impl.js": "bc4efc13c8174437fe451ea2106e60473b06b47ec31779ee58b05d455a224d57", 373 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/URL.js": "5dd272f5016360709230cc7478e394cefcf13b75d66e2e363733a864addffcc3", 374 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/URLSearchParams-impl.js": "0811d21279a134b7c054290d24c213212814fdb2d062da5acd3c8624f2cb8a47", 375 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/URLSearchParams.js": "1c7360843aee9e3ddc93e808adbdee806ae9a763f278bb5001ec6afed8e938e9", 376 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/encoding.js": "c0ab35bbf2658b798688cd6ebb299b818bef31bca6b4e1d4fd814f716e233c41", 377 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/infra.js": "3ecb84fe1f5b714d5cbb0957b346465a90b7623b1d016a5c82e87b95a7e541fd", 378 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/percent-encoding.js": "1706bb5567ffa6f18392b2c92f4087da8f60644834143e55be2fd67c7d5622ac", 379 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/url-state-machine.js": "1116a96bf9141fab9e03802c863eb6ca3eafbfa23c327c76a8378fc28665bc19", 380 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/urlencoded.js": "df76343c9e806c2955fefb703f6ff493e903c9f14757757c6c001c4336b51a96", 381 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/lib/utils.js": "332628b61cae7bf7b5f18ad9b144f89c06470a345ec1515a4f2de5976be3b1fa", 382 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/webidl2js-wrapper.js": "384548d678ee7b93b6291bb5b4565262e1f4f4b97bae0657c285cc981e829e05", 383 | "https://esm.sh/v99/whatwg-url@10.0.0/deno/whatwg-url.js": "a4de2de9d408c52decd9f0315bbc402ee08e57e666042cebe7426978a6c9fe39", 384 | "https://esm.sh/v99/whatwg-url@11.0.0/deno/whatwg-url.js": "9a1476419e33f0b8dc922ffafaca69213de37fb8f0c37c585e6ddc0c4701e2f6", 385 | "https://esm.sh/v99/xml-name-validator@4.0.0/deno/xml-name-validator.js": "3f52a4008498d096db9369a1965ebff54888c344f194bc16a42389903077e16f", 386 | "https://esm.sh/v99/xmlchars@2.2.0/deno/xml/1.0/ed5.js": "d8fc987285a1c47c88251f8052a35450af0f01844ed7610ebda9f863f189aea7", 387 | "https://esm.sh/v99/xmlchars@2.2.0/deno/xml/1.1/ed2.js": "530b2f4cb4933655ac0e1c88da97f3586e78e9d9265c0286ea64f016afff1244", 388 | "https://esm.sh/v99/xmlchars@2.2.0/deno/xmlns/1.0/ed3.js": "7425d04c57d6babb72fd2deab46e9ce71c93eaa4400806bac1e4440f02c0e601", 389 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/compiler.ts": "dd589db479d6d7e69999865003ab83c41544e251ece4f21f2f2ee74557097ba6", 390 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/compiler_transforms.ts": "cbb1fd5948f5ced1aa5c5aed9e45134e2357ce1e7220924c1d7bded30dcd0dd0", 391 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/mod.deps.ts": "6648fb17b4a49677cb0c24f60ffb5067a86ad69ff97712d40fe0d62b281b1811", 392 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/npm_ignore.ts": "ddc1a7a76b288ca471bf1a6298527887a0f9eb7e25008072fd9c9fa9bb28c71a", 393 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/package_json.ts": "2d629dbaef8004971e38ce3661f04b915a35342b905c3d98ff4a25343c2a8293", 394 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/pkg/dnt_wasm.generated.js": "00257fc2f03321bb5f2b9bc23cb85e79fe55eb49a325d5ab925b9fc81b4aa963", 395 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be", 396 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/shims.ts": "7998851b149cb230f5183590d3b66c06f696fefb1c31c24eb5736f1ef12a4de1", 397 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/test_runner/get_test_runner_code.ts": "2a4e26aa33120f3cc9e03b8538211a5047a4bad4c64e895944b87f2dcd55d904", 398 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/test_runner/test_runner.ts": "b91d77d9d4b82984cb2ba7431ba6935756ba72f62e7dd4db22cd47a680ebd952", 399 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/transform.deps.ts": "a6e138e380ebe4479bed1b00ae8dff1d4e6626cc53bc79dd98f907b43745d63a", 400 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/types.ts": "34e45a3136c2f21f797173ea46d9ea5d1639eb7b834a5bd565aad4214fa32603", 401 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/lib/utils.ts": "d13b5b3148a2c71e9b2f1c84c7be7393b825ae972505e23c2f6b1e5287e96b43", 402 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/mod.ts": "691ea4b644cc61123b7beed19e66af301f25985483b81d21cfe49a0be2877fd9", 403 | "https://raw.githubusercontent.com/denoland/dnt/0.33.1/transform.ts": "1b127c5f22699c8ab2545b98aeca38c4e5c21405b0f5342ea17e9c46280ed277" 404 | }, 405 | "npm": { 406 | "specifiers": { 407 | "compago@5.0.5": "compago@5.0.5", 408 | "lit@2.7.5": "lit@2.7.5" 409 | }, 410 | "packages": { 411 | "@lit-labs/ssr-dom-shim@1.1.1": { 412 | "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==", 413 | "dependencies": {} 414 | }, 415 | "@lit/reactive-element@1.6.2": { 416 | "integrity": "sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==", 417 | "dependencies": { 418 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.1.1" 419 | } 420 | }, 421 | "@types/trusted-types@2.0.3": { 422 | "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", 423 | "dependencies": {} 424 | }, 425 | "compago@5.0.5": { 426 | "integrity": "sha512-AIsoUmoOmwDIviu0nFYx6Xqh2uTJFZrLk3OQiJfO0DTEvOCaimE4gfKSdw1p1ckv7njwjKBnIWpqGSuGpu2pqQ==", 427 | "dependencies": { 428 | "lit": "lit@2.7.5" 429 | } 430 | }, 431 | "lit-element@3.3.2": { 432 | "integrity": "sha512-xXAeVWKGr4/njq0rGC9dethMnYCq5hpKYrgQZYTzawt9YQhMiXfD+T1RgrdY3NamOxwq2aXlb0vOI6e29CKgVQ==", 433 | "dependencies": { 434 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.1.1", 435 | "@lit/reactive-element": "@lit/reactive-element@1.6.2", 436 | "lit-html": "lit-html@2.7.4" 437 | } 438 | }, 439 | "lit-html@2.7.4": { 440 | "integrity": "sha512-/Jw+FBpeEN+z8X6PJva5n7+0MzCVAH2yypN99qHYYkq8bI+j7I39GH+68Z/MZD6rGKDK9RpzBw7CocfmHfq6+g==", 441 | "dependencies": { 442 | "@types/trusted-types": "@types/trusted-types@2.0.3" 443 | } 444 | }, 445 | "lit@2.7.5": { 446 | "integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==", 447 | "dependencies": { 448 | "@lit/reactive-element": "@lit/reactive-element@1.6.2", 449 | "lit-element": "lit-element@3.3.2", 450 | "lit-html": "lit-html@2.7.4" 451 | } 452 | } 453 | } 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /examples/todo/README.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | The classic Todo app example using Deno, Lit, and Compago. 4 | 5 | ## Development 6 | 7 | ### Formatting, Linting, Testing 8 | 9 | You know 10 | [the drill](https://github.com/zandaqo/compago/tree/master/examples/counter#development)--all 11 | the standard Deno tools are at your disposal. Mind the current 12 | [limitations](https://github.com/zandaqo/compago/tree/master/examples/counter#known-issues), 13 | though. 14 | 15 | ### Building 16 | 17 | ``` 18 | deno task build 19 | ``` 20 | 21 | This will bundle our app into `main.js` file that is imported into `index.html`. 22 | 23 | Under the hood, we use [esbuild](https://esbuild.github.io/) with the 24 | [esbuild_deno_loader](https://deno.land/x/esbuild_deno_loader) to create the 25 | bundle. There are other ways to do it using Deno, but `esbuild` is better and 26 | works fine with deno. 27 | -------------------------------------------------------------------------------- /examples/todo/_build.ts: -------------------------------------------------------------------------------- 1 | import * as esbuild from "https://deno.land/x/esbuild@v0.17.19/mod.js"; 2 | import { denoPlugins } from "https://deno.land/x/esbuild_deno_loader@0.8.0/mod.ts"; 3 | 4 | esbuild 5 | .build({ 6 | plugins: [...denoPlugins({ 7 | nodeModulesDir: true, 8 | })], 9 | entryPoints: ["./main.ts"], 10 | outfile: "./main.js", 11 | target: "es2022", 12 | format: "esm", 13 | bundle: true, 14 | minify: true, 15 | sourcemap: true, 16 | }) 17 | .then(() => Deno.exit(0)) 18 | .catch(() => Deno.exit(1)); 19 | -------------------------------------------------------------------------------- /examples/todo/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "dom.asynciterable", 7 | "deno.ns" 8 | ] 9 | }, 10 | "tasks": { 11 | "build": "deno run -A _build.ts" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/todo/deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "remote": { 4 | "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 5 | "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 6 | "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", 7 | "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", 8 | "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", 9 | "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", 10 | "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", 11 | "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", 12 | "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", 13 | "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", 14 | "https://deno.land/std@0.140.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", 15 | "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 16 | "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 17 | "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 18 | "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 19 | "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 20 | "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", 21 | "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 22 | "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 23 | "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 24 | "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", 25 | "https://deno.land/std@0.156.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 26 | "https://deno.land/std@0.156.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 27 | "https://deno.land/std@0.156.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 28 | "https://deno.land/std@0.156.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 29 | "https://deno.land/std@0.156.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", 30 | "https://deno.land/std@0.156.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 31 | "https://deno.land/std@0.156.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 32 | "https://deno.land/std@0.156.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", 33 | "https://deno.land/std@0.156.0/path/posix.ts": "c1f7afe274290ea0b51da07ee205653b2964bd74909a82deb07b69a6cc383aaa", 34 | "https://deno.land/std@0.156.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 35 | "https://deno.land/std@0.156.0/path/win32.ts": "bd7549042e37879c68ff2f8576a25950abbfca1d696d41d82c7bca0b7e6f452c", 36 | "https://deno.land/std@0.173.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 37 | "https://deno.land/std@0.173.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 38 | "https://deno.land/std@0.173.0/encoding/base32.ts": "cb15f16e53c580d2491637280302bc6cfeb48f8911521ea9c7895ba513a2bcc5", 39 | "https://deno.land/std@0.173.0/encoding/jsonc.ts": "02b86115d2b812f26789481ebcf4748171e8ece6514b60243b3733e2c200876a", 40 | "https://deno.land/std@0.173.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 41 | "https://deno.land/std@0.173.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", 42 | "https://deno.land/std@0.173.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 43 | "https://deno.land/std@0.173.0/fs/ensure_dir.ts": "724209875497a6b4628dfb256116e5651c4f7816741368d6c44aab2531a1e603", 44 | "https://deno.land/std@0.173.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", 45 | "https://deno.land/std@0.173.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", 46 | "https://deno.land/std@0.173.0/fs/ensure_symlink.ts": "2955cc8332aeca9bdfefd05d8d3976b94e282b0f353392a71684808ed2ffdd41", 47 | "https://deno.land/std@0.173.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", 48 | "https://deno.land/std@0.173.0/fs/exists.ts": "b8c8a457b71e9d7f29b9d2f87aad8dba2739cbe637e8926d6ba6e92567875f8e", 49 | "https://deno.land/std@0.173.0/fs/expand_glob.ts": "45d17e89796a24bd6002e4354eda67b4301bb8ba67d2cac8453cdabccf1d9ab0", 50 | "https://deno.land/std@0.173.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", 51 | "https://deno.land/std@0.173.0/fs/move.ts": "4cb47f880e3f0582c55e71c9f8b1e5e8cfaacb5e84f7390781dd563b7298ec19", 52 | "https://deno.land/std@0.173.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 53 | "https://deno.land/std@0.173.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 54 | "https://deno.land/std@0.173.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 55 | "https://deno.land/std@0.173.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8", 56 | "https://deno.land/std@0.173.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 57 | "https://deno.land/std@0.173.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 58 | "https://deno.land/std@0.173.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232", 59 | "https://deno.land/std@0.173.0/path/posix.ts": "0874b341c2c6968ca38d323338b8b295ea1dae10fa872a768d812e2e7d634789", 60 | "https://deno.land/std@0.173.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 61 | "https://deno.land/std@0.173.0/path/win32.ts": "672942919dd66ce1b8c224e77d3447e2ad8254eaff13fe6946420a9f78fa141e", 62 | "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", 63 | "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", 64 | "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", 65 | "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", 66 | "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", 67 | "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", 68 | "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", 69 | "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", 70 | "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", 71 | "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", 72 | "https://deno.land/x/deno_graph@0.26.0/lib/deno_graph.generated.js": "2f7ca85b2ceb80ec4b3d1b7f3a504956083258610c7b9a1246238c5b7c68f62d", 73 | "https://deno.land/x/deno_graph@0.26.0/lib/loader.ts": "380e37e71d0649eb50176a9786795988fc3c47063a520a54b616d7727b0f8629", 74 | "https://deno.land/x/deno_graph@0.26.0/lib/media_type.ts": "222626d524fa2f9ebcc0ec7c7a7d5dfc74cc401cc46790f7c5e0eab0b0787707", 75 | "https://deno.land/x/deno_graph@0.26.0/lib/snippets/deno_graph-de651bc9c240ed8d/src/deno_apis.js": "41192baaa550a5c6a146280fae358cede917ae16ec4e4315be51bef6631ca892", 76 | "https://deno.land/x/deno_graph@0.26.0/lib/types.d.ts": "2bbdbf895321d1df8db511fab00160a0211c09c2e7cac56c522dd6e9ed6d2ef7", 77 | "https://deno.land/x/deno_graph@0.26.0/mod.ts": "11131ae166580a1c7fa8506ff553751465a81c263d94443f18f353d0c320bc14", 78 | "https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6", 79 | "https://deno.land/x/denoflate@1.2.1/pkg/denoflate.js": "b9f9ad9457d3f12f28b1fb35c555f57443427f74decb403113d67364e4f2caf4", 80 | "https://deno.land/x/denoflate@1.2.1/pkg/denoflate_bg.wasm.js": "d581956245407a2115a3d7e8d85a9641c032940a8e810acbd59ca86afd34d44d", 81 | "https://deno.land/x/esbuild@v0.15.10/mod.d.ts": "12e96a8d05c8e2c5638aa9cb09f24222134d437abb2f3f52de0156adb2270719", 82 | "https://deno.land/x/esbuild@v0.15.15/mod.d.ts": "12e96a8d05c8e2c5638aa9cb09f24222134d437abb2f3f52de0156adb2270719", 83 | "https://deno.land/x/esbuild@v0.15.15/mod.js": "1e72d17938ae3a5b8b78aa910a5fadcdd4c22e431f3cd700f1d92bc4f124d777", 84 | "https://deno.land/x/esbuild@v0.17.19/mod.d.ts": "dc279a3a46f084484453e617c0cabcd5b8bd1920c0e562e4ea02dfc828c8f968", 85 | "https://deno.land/x/esbuild@v0.17.19/mod.js": "7f4398625828a42fcb2c32da5eb874f16341d778e473d5403e5ebeee95a5b96b", 86 | "https://deno.land/x/esbuild_deno_loader@0.6.0/deps.ts": "fe86f62cb954edc2580868cdc3292d4446f22cd2c4eedbf8ee26513fdf74e343", 87 | "https://deno.land/x/esbuild_deno_loader@0.6.0/mod.ts": "5d8a429b26c70f9fd522a8629e751f3bdf63680da473e537bc420d552ee3bf0b", 88 | "https://deno.land/x/esbuild_deno_loader@0.6.0/src/deno.ts": "0e83ccabbe2b004389288e38df2031b79eb347df2d139fce9394d8e88a11f259", 89 | "https://deno.land/x/esbuild_deno_loader@0.6.0/src/native_loader.ts": "343854a566cf510cf25144f7c09fc0c1097780a31830305142a075d12bb697ba", 90 | "https://deno.land/x/esbuild_deno_loader@0.6.0/src/portable_loader.ts": "35b6c526eed8c2c781a3256b23c30aa7cce69c0ef1d583c15528663287ba18a3", 91 | "https://deno.land/x/esbuild_deno_loader@0.6.0/src/shared.ts": "b64749cd8c0f6252a11498bd8758ef1220003e46b2c9b68e16da63fd7e92b13a", 92 | "https://deno.land/x/esbuild_deno_loader@0.8.0/deps.ts": "987b50a1a921fcc84ddfcf1a1256cb955f6b16acac28a3fc77901c12c0752173", 93 | "https://deno.land/x/esbuild_deno_loader@0.8.0/mod.ts": "28524460bef46d487221b01ade6ed913d2e127de7eeee025ab75b34b491283da", 94 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/deno.ts": "b0af3e430c068f18c6fa48c2083a1b4354b6c303e16fb37855e02fcafb95f36d", 95 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/loader_native.ts": "6b606daf36da947b449500cf6fc53c561fd31947dd5eeaaed1bcd08c9e075a45", 96 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/loader_portable.ts": "d999f452ef3d8ec2dd3c8443f542adf57efc8a2cd59b29cc41f5b3d7dff512e5", 97 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/plugin_deno_loader.ts": "1bf211870497ae996cf7e6b54f35a571ff183ee64fef88d50e57825244654169", 98 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/plugin_deno_resolver.ts": "0449ed23ae93db1ec74d015a46934aefd7ba7a8f719f7a4980b616cb3f5bbee4", 99 | "https://deno.land/x/esbuild_deno_loader@0.8.0/src/shared.ts": "6df7eba25070c55015176498e7d14a0f8bcaf74c697ebee21a477984f577ec6a", 100 | "https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89", 101 | "https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade" 102 | }, 103 | "npm": { 104 | "specifiers": { 105 | "compago@5.0.5": "compago@5.0.5", 106 | "esbuild@0.17.19": "esbuild@0.17.19", 107 | "lit@2.6.1": "lit@2.6.1", 108 | "lit@2.7.5": "lit@2.7.5" 109 | }, 110 | "packages": { 111 | "@esbuild/android-arm64@0.17.19": { 112 | "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", 113 | "dependencies": {} 114 | }, 115 | "@esbuild/android-arm@0.17.19": { 116 | "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", 117 | "dependencies": {} 118 | }, 119 | "@esbuild/android-x64@0.17.19": { 120 | "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", 121 | "dependencies": {} 122 | }, 123 | "@esbuild/darwin-arm64@0.17.19": { 124 | "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", 125 | "dependencies": {} 126 | }, 127 | "@esbuild/darwin-x64@0.17.19": { 128 | "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", 129 | "dependencies": {} 130 | }, 131 | "@esbuild/freebsd-arm64@0.17.19": { 132 | "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", 133 | "dependencies": {} 134 | }, 135 | "@esbuild/freebsd-x64@0.17.19": { 136 | "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", 137 | "dependencies": {} 138 | }, 139 | "@esbuild/linux-arm64@0.17.19": { 140 | "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", 141 | "dependencies": {} 142 | }, 143 | "@esbuild/linux-arm@0.17.19": { 144 | "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", 145 | "dependencies": {} 146 | }, 147 | "@esbuild/linux-ia32@0.17.19": { 148 | "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", 149 | "dependencies": {} 150 | }, 151 | "@esbuild/linux-loong64@0.17.19": { 152 | "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", 153 | "dependencies": {} 154 | }, 155 | "@esbuild/linux-mips64el@0.17.19": { 156 | "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", 157 | "dependencies": {} 158 | }, 159 | "@esbuild/linux-ppc64@0.17.19": { 160 | "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", 161 | "dependencies": {} 162 | }, 163 | "@esbuild/linux-riscv64@0.17.19": { 164 | "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", 165 | "dependencies": {} 166 | }, 167 | "@esbuild/linux-s390x@0.17.19": { 168 | "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", 169 | "dependencies": {} 170 | }, 171 | "@esbuild/linux-x64@0.17.19": { 172 | "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", 173 | "dependencies": {} 174 | }, 175 | "@esbuild/netbsd-x64@0.17.19": { 176 | "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", 177 | "dependencies": {} 178 | }, 179 | "@esbuild/openbsd-x64@0.17.19": { 180 | "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", 181 | "dependencies": {} 182 | }, 183 | "@esbuild/sunos-x64@0.17.19": { 184 | "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", 185 | "dependencies": {} 186 | }, 187 | "@esbuild/win32-arm64@0.17.19": { 188 | "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", 189 | "dependencies": {} 190 | }, 191 | "@esbuild/win32-ia32@0.17.19": { 192 | "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", 193 | "dependencies": {} 194 | }, 195 | "@esbuild/win32-x64@0.17.19": { 196 | "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", 197 | "dependencies": {} 198 | }, 199 | "@lit-labs/ssr-dom-shim@1.0.0": { 200 | "integrity": "sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==", 201 | "dependencies": {} 202 | }, 203 | "@lit-labs/ssr-dom-shim@1.1.1": { 204 | "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==", 205 | "dependencies": {} 206 | }, 207 | "@lit/reactive-element@1.6.1": { 208 | "integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", 209 | "dependencies": { 210 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.0.0" 211 | } 212 | }, 213 | "@types/trusted-types@2.0.2": { 214 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", 215 | "dependencies": {} 216 | }, 217 | "compago@5.0.5": { 218 | "integrity": "sha512-AIsoUmoOmwDIviu0nFYx6Xqh2uTJFZrLk3OQiJfO0DTEvOCaimE4gfKSdw1p1ckv7njwjKBnIWpqGSuGpu2pqQ==", 219 | "dependencies": { 220 | "lit": "lit@2.7.5" 221 | } 222 | }, 223 | "esbuild@0.17.19": { 224 | "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", 225 | "dependencies": { 226 | "@esbuild/android-arm": "@esbuild/android-arm@0.17.19", 227 | "@esbuild/android-arm64": "@esbuild/android-arm64@0.17.19", 228 | "@esbuild/android-x64": "@esbuild/android-x64@0.17.19", 229 | "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.17.19", 230 | "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.17.19", 231 | "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.17.19", 232 | "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.17.19", 233 | "@esbuild/linux-arm": "@esbuild/linux-arm@0.17.19", 234 | "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.17.19", 235 | "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.17.19", 236 | "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.17.19", 237 | "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.17.19", 238 | "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.17.19", 239 | "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.17.19", 240 | "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.17.19", 241 | "@esbuild/linux-x64": "@esbuild/linux-x64@0.17.19", 242 | "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.17.19", 243 | "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.17.19", 244 | "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.17.19", 245 | "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.17.19", 246 | "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.17.19", 247 | "@esbuild/win32-x64": "@esbuild/win32-x64@0.17.19" 248 | } 249 | }, 250 | "lit-element@3.2.2": { 251 | "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", 252 | "dependencies": { 253 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 254 | "lit-html": "lit-html@2.6.1" 255 | } 256 | }, 257 | "lit-element@3.3.2": { 258 | "integrity": "sha512-xXAeVWKGr4/njq0rGC9dethMnYCq5hpKYrgQZYTzawt9YQhMiXfD+T1RgrdY3NamOxwq2aXlb0vOI6e29CKgVQ==", 259 | "dependencies": { 260 | "@lit-labs/ssr-dom-shim": "@lit-labs/ssr-dom-shim@1.1.1", 261 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 262 | "lit-html": "lit-html@2.7.4" 263 | } 264 | }, 265 | "lit-html@2.6.1": { 266 | "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", 267 | "dependencies": { 268 | "@types/trusted-types": "@types/trusted-types@2.0.2" 269 | } 270 | }, 271 | "lit-html@2.7.4": { 272 | "integrity": "sha512-/Jw+FBpeEN+z8X6PJva5n7+0MzCVAH2yypN99qHYYkq8bI+j7I39GH+68Z/MZD6rGKDK9RpzBw7CocfmHfq6+g==", 273 | "dependencies": { 274 | "@types/trusted-types": "@types/trusted-types@2.0.2" 275 | } 276 | }, 277 | "lit@2.6.1": { 278 | "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", 279 | "dependencies": { 280 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 281 | "lit-element": "lit-element@3.2.2", 282 | "lit-html": "lit-html@2.6.1" 283 | } 284 | }, 285 | "lit@2.7.5": { 286 | "integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==", 287 | "dependencies": { 288 | "@lit/reactive-element": "@lit/reactive-element@1.6.1", 289 | "lit-element": "lit-element@3.3.2", 290 | "lit-html": "lit-html@2.7.4" 291 | } 292 | } 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /examples/todo/deps.ts: -------------------------------------------------------------------------------- 1 | export { css, html, LitElement } from "npm:lit@2.7.5"; 2 | export { customElement, property, state } from "npm:lit@2.7.5/decorators.js"; 3 | export { repeat } from "npm:lit@2.7.5/directives/repeat.js"; 4 | export { createRef, ref } from "npm:lit@2.7.5/directives/ref.js"; 5 | export { bond, Observable, observe, observer } from "npm:compago@5.0.5"; 6 | -------------------------------------------------------------------------------- /examples/todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todos Compago & Lit 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/todo/main.ts: -------------------------------------------------------------------------------- 1 | import "./todo-app.ts"; 2 | 3 | document.body.appendChild(document.createElement("todo-app")); 4 | -------------------------------------------------------------------------------- /examples/todo/todo-app.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createRef, 3 | css, 4 | customElement, 5 | html, 6 | LitElement, 7 | Observable, 8 | observe, 9 | observer, 10 | ref, 11 | repeat, 12 | } from "./deps.ts"; 13 | import { Todo } from "./todo.ts"; 14 | import "./todo-item.ts"; 15 | 16 | @customElement("todo-app") 17 | @observer() 18 | export class TodoApp extends LitElement { 19 | @observe() 20 | state = new Observable({ items: [] as Array }); 21 | input = createRef(); 22 | static styles = css` 23 | h1 { 24 | font-size: 70px; 25 | line-height: 70px; 26 | font-weight: 100; 27 | text-align: center; 28 | color: rgba(175, 47, 47, 0.15); 29 | } 30 | section { 31 | background: #fff; 32 | margin: 30px 0 40px 0; 33 | position: relative; 34 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 35 | } 36 | input[type=text] { 37 | padding: 16px 16px 16px 60px; 38 | border: none; 39 | background: rgba(0, 0, 0, 0.003); 40 | position: relative; 41 | margin: 0; 42 | width: 100%; 43 | font-size: 24px; 44 | font-family: inherit; 45 | font-weight: inherit; 46 | line-height: 1.4em; 47 | border: 0; 48 | outline: none; 49 | color: inherit; 50 | padding: 6px; 51 | border: 1px solid #CCC; 52 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 53 | box-sizing: border-box; 54 | } 55 | `; 56 | 57 | onSubmit(event: KeyboardEvent) { 58 | if (event.code !== "Enter") return; 59 | const input = this.input.value; 60 | if (input && input.value) { 61 | this.state.items.push(new Todo({ text: input.value })); 62 | input.value = ""; 63 | } 64 | } 65 | 66 | onRemove(event: CustomEvent<{ id: string }>) { 67 | const id = event.detail.id; 68 | if (!id) return; 69 | const index = this.state.items.findIndex((item) => item.id === id); 70 | if (~index) this.state.items.splice(index, 1); 71 | } 72 | 73 | render() { 74 | return html` 75 |

Todos Compago & Lit

76 |
77 | 79 | ${ 80 | repeat(this.state.items, (todo) => 81 | todo.id, (todo) => 82 | html``) 83 | } 84 |
85 | `; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /examples/todo/todo-item.ts: -------------------------------------------------------------------------------- 1 | import { 2 | bond, 3 | css, 4 | customElement, 5 | html, 6 | LitElement, 7 | observe, 8 | observer, 9 | state, 10 | } from "./deps.ts"; 11 | import { Todo } from "./todo.ts"; 12 | 13 | @observer() 14 | @customElement("todo-item") 15 | export class TodoItem extends LitElement { 16 | @observe() 17 | todo!: Todo; 18 | 19 | @state() 20 | isEditing = false; 21 | 22 | static styles = css` 23 | :host { 24 | display: block; 25 | position: relative; 26 | font-size: 24px; 27 | border-bottom: 1px solid #ededed; 28 | } 29 | input { 30 | text-align: center; 31 | width: 40px; 32 | /* auto, since non-WebKit browsers doesn't support input styling */ 33 | height: auto; 34 | position: absolute; 35 | top: 9px; 36 | bottom: 0; 37 | margin: auto 0; 38 | border: none; 39 | /* Mobile Safari */ 40 | -webkit-appearance: none; 41 | appearance: none; 42 | } 43 | input:after { 44 | content: url('data:image/svg+xml;utf8,'); 45 | } 46 | input:checked:after { 47 | content: url('data:image/svg+xml;utf8,'); 48 | } 49 | label { 50 | white-space: pre; 51 | word-break: break-word; 52 | padding: 15px 60px 15px 15px; 53 | margin-left: 45px; 54 | display: block; 55 | line-height: 1.2; 56 | transition: color 0.4s; 57 | } 58 | input:checked + label { 59 | color: #d9d9d9; 60 | text-decoration: line-through; 61 | } 62 | button, 63 | input[type="checkbox"] { 64 | outline: none; 65 | } 66 | button { 67 | margin: 0; 68 | padding: 0; 69 | border: 0; 70 | background: none; 71 | font-size: 100%; 72 | vertical-align: baseline; 73 | font-family: inherit; 74 | font-weight: inherit; 75 | color: inherit; 76 | -webkit-appearance: none; 77 | appearance: none; 78 | -webkit-font-smoothing: antialiased; 79 | -moz-font-smoothing: antialiased; 80 | font-smoothing: antialiased; 81 | } 82 | button { 83 | position: absolute; 84 | top: 0; 85 | right: 10px; 86 | bottom: 0; 87 | width: 40px; 88 | height: 40px; 89 | margin: auto 0; 90 | font-size: 30px; 91 | color: #cc9a9a; 92 | margin-bottom: 11px; 93 | transition: color 0.2s ease-out; 94 | } 95 | button:hover { 96 | color: #af5b5e; 97 | } 98 | `; 99 | 100 | onRemove() { 101 | this.dispatchEvent( 102 | new CustomEvent("remove", { 103 | detail: { id: this.todo.id }, 104 | bubbles: true, 105 | composed: true, 106 | }), 107 | ); 108 | } 109 | 110 | render() { 111 | return html` 112 | 119 | 120 | 121 | `; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /examples/todo/todo.ts: -------------------------------------------------------------------------------- 1 | export class Todo { 2 | id = globalThis.crypto.randomUUID(); 3 | text = ""; 4 | done = false; 5 | 6 | constructor(todo: Partial) { 7 | Object.assign(this, todo); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /local-repository.ts: -------------------------------------------------------------------------------- 1 | import type { Constructor } from "./constructor.ts"; 2 | import type { Repository } from "./repository.ts"; 3 | import { Result } from "./result.ts"; 4 | 5 | /** 6 | * Implementation of the Repository interface 7 | * for operating on data stored locally in IndexedDB. 8 | */ 9 | export class LocalRepository implements Repository { 10 | constructor( 11 | public Entity: Constructor, 12 | public db: IDBDatabase, 13 | public store: string, 14 | public key: keyof T, 15 | ) { 16 | } 17 | 18 | async has(key: string): Promise> { 19 | if (!key) return Result.ok(false); 20 | const request = await LocalRepository.promisify( 21 | this.db.transaction(this.store, "readonly") 22 | .objectStore(this.store) 23 | .openCursor(key), 24 | ); 25 | if (!request.ok) return request; 26 | return Result.ok(!!request.value); 27 | } 28 | 29 | async list( 30 | ...args: Array 31 | ): Promise, unknown>> { 32 | const list: Array = []; 33 | for (const key of args) { 34 | const result = await LocalRepository.promisify( 35 | this.db.transaction(this.store, "readonly") 36 | .objectStore(this.store) 37 | .getAll(key), 38 | ); 39 | if (result.ok && result.value) { 40 | list.push(...result.value.map( 41 | (item) => this.deserialize(item), 42 | )); 43 | } 44 | } 45 | return Result.ok(list); 46 | } 47 | 48 | async create(value: T) { 49 | return await LocalRepository.promisify( 50 | this.db.transaction(this.store, "readwrite") 51 | .objectStore(this.store) 52 | .add(this.serialize(value)), 53 | ); 54 | } 55 | 56 | async read(id: string): Promise> { 57 | const request = await LocalRepository.promisify( 58 | this.db.transaction(this.store, "readonly") 59 | .objectStore(this.store) 60 | .get(id), 61 | ); 62 | if (!request.ok) return request; 63 | if (!request.value) return Result.fail(undefined); 64 | return Result.ok(this.deserialize(request.value)); 65 | } 66 | 67 | async update(_id: string, updates: Partial) { 68 | return await LocalRepository.promisify( 69 | this.db.transaction(this.store, "readwrite") 70 | .objectStore(this.store) 71 | .put(this.serialize(updates)), 72 | ); 73 | } 74 | 75 | async delete(id: string) { 76 | return await LocalRepository.promisify( 77 | this.db.transaction(this.store, "readwrite") 78 | .objectStore(this.store) 79 | .delete(id), 80 | ); 81 | } 82 | 83 | serialize(entity: Partial): unknown { 84 | return entity; 85 | } 86 | 87 | deserialize(value: unknown): T { 88 | return new this.Entity(value); 89 | } 90 | 91 | static promisify( 92 | request: IDBRequest, 93 | ): Promise> { 94 | return new Promise>((resolve) => { 95 | const unlisten = () => { 96 | request.removeEventListener("success", success); 97 | request.removeEventListener("error", error); 98 | }; 99 | const success = () => { 100 | resolve(Result.ok(request.result)); 101 | unlisten(); 102 | }; 103 | const error = () => { 104 | resolve(Result.fail(request.error)); 105 | unlisten(); 106 | }; 107 | request.addEventListener("success", success); 108 | request.addEventListener("error", error); 109 | }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export { bond } from "./bond-directive.ts"; 2 | export { ChangeEvent, ChangeType, Observable } from "./observable.ts"; 3 | export { observe, observer, ObserverElement } from "./observer.ts"; 4 | export { Result } from "./result.ts"; 5 | export type { Entity, Repository } from "./repository.ts"; 6 | export type { Constructor } from "./constructor.ts"; 7 | export { RESTRepository } from "./rest-repository.ts"; 8 | export { LocalRepository } from "./local-repository.ts"; 9 | -------------------------------------------------------------------------------- /observable.ts: -------------------------------------------------------------------------------- 1 | export const sPath = Symbol.for("c-path"); 2 | export const sObservable = Symbol.for("c-observable"); 3 | 4 | export interface ObservedValue { 5 | [sPath]: string; 6 | [sObservable]: _Observable; 7 | [key: PropertyKey]: unknown; 8 | } 9 | 10 | const watchedArrayMethods = new Set([ 11 | "push", 12 | "pop", 13 | "unshift", 14 | "shift", 15 | "splice", 16 | "sort", 17 | ]); 18 | 19 | interface ObservableEventMap { 20 | change: ChangeEvent; 21 | } 22 | 23 | export const ChangeType = { 24 | Set: "SET", 25 | Delete: "DELETE", 26 | Add: "ADD", 27 | Remove: "REMOVE", 28 | Sort: "SORT", 29 | } as const; 30 | 31 | export type ChangeType = typeof ChangeType[keyof typeof ChangeType]; 32 | 33 | /** 34 | * The change event is fired by an observable when any change happens to the observable. 35 | */ 36 | export class ChangeEvent extends Event { 37 | path: string; 38 | kind: ChangeType; 39 | previous?: unknown; 40 | elements?: unknown; 41 | 42 | constructor( 43 | path: string, 44 | kind: ChangeType, 45 | previous?: unknown, 46 | elements?: unknown, 47 | ) { 48 | super("change", { bubbles: true, composed: true }); 49 | this.path = path; 50 | this.kind = kind; 51 | this.previous = previous; 52 | this.elements = elements; 53 | } 54 | } 55 | 56 | export interface _Observable { 57 | addEventListener( 58 | type: K, 59 | listener: (this: _Observable, ev: ObservableEventMap[K]) => unknown, 60 | options?: boolean | AddEventListenerOptions, 61 | ): void; 62 | 63 | addEventListener( 64 | type: string, 65 | listener: EventListenerOrEventListenerObject, 66 | options?: boolean | AddEventListenerOptions, 67 | ): void; 68 | 69 | removeEventListener( 70 | type: K, 71 | listener: (this: _Observable, ev: ObservableEventMap[K]) => unknown, 72 | options?: boolean | AddEventListenerOptions, 73 | ): void; 74 | 75 | removeEventListener( 76 | type: string, 77 | listener: EventListenerOrEventListenerObject, 78 | options?: boolean | AddEventListenerOptions, 79 | ): void; 80 | } 81 | 82 | export class _Observable extends EventTarget { 83 | declare [sPath]: string; 84 | declare [sObservable]: _Observable; 85 | 86 | static readonly arrayHandler: ProxyHandler = { 87 | get: _Observable.arrayGetTrap, 88 | set: _Observable.setTrap, 89 | deleteProperty: _Observable.deletePropertyTrap, 90 | }; 91 | 92 | static readonly handler: ProxyHandler = { 93 | set: _Observable.setTrap, 94 | deleteProperty: _Observable.deletePropertyTrap, 95 | }; 96 | 97 | /** 98 | * @param properties initial data for the observable 99 | */ 100 | constructor(properties: T) { 101 | super(); 102 | Reflect.defineProperty(this, "addEventListener", { 103 | value: this.addEventListener.bind(this), 104 | writable: true, 105 | }); 106 | Reflect.defineProperty(this, "removeEventListener", { 107 | value: this.removeEventListener.bind(this), 108 | writable: true, 109 | }); 110 | Reflect.defineProperty(this, "dispatchEvent", { 111 | value: this.dispatchEvent.bind(this), 112 | writable: true, 113 | }); 114 | this.assign(properties); 115 | return _Observable.getProxy(this, "", this, [this]); 116 | } 117 | 118 | /** 119 | * Resets all properties on the observable with given ones. 120 | * 121 | * @param properties the properties to be set on the observable 122 | */ 123 | set(properties: T): this { 124 | for (const key of Object.keys(this) as Array) { 125 | if (!Reflect.has(properties, key)) delete this[key]; 126 | } 127 | return Object.assign(this, properties); 128 | } 129 | 130 | /** 131 | * Assigns given properties to the observable. 132 | * 133 | * @param {Object} properties the properties to be assigned to the observable 134 | */ 135 | assign(properties: Partial) { 136 | return Object.assign(this, properties); 137 | } 138 | 139 | /** 140 | * Merges two objects. If no target object provided, merges a given source object with the observable. 141 | * 142 | * @param source the source object to be merged with the target object. 143 | * @param target the target object to be merged, uses the observable by default 144 | */ 145 | merge(source: object, target: object = this): object { 146 | for (const key of Object.keys(source) as Array) { 147 | const current = source[key]; 148 | const existing = target[key]; 149 | // deno-lint-ignore no-explicit-any 150 | (target as any)[key] = _Observable.isObjectObservable(existing) && 151 | _Observable.isObjectObservable(current) 152 | ? this.merge(current, existing) 153 | : (target[key] = current); 154 | } 155 | return target; 156 | } 157 | 158 | /** 159 | * Returns a copy of the observable for JSON stringification. 160 | */ 161 | toJSON() { 162 | return { ...this }; 163 | } 164 | 165 | /** 166 | * Checks whether a value can become an observable. 167 | */ 168 | static isObjectObservable(value: unknown): boolean { 169 | const type = Object.prototype.toString.call(value); 170 | return type === "[object Object]" || type === "[object Array]"; 171 | } 172 | 173 | /** 174 | * Checks two values for 'deep' equality. 175 | * 176 | * Adopted from [fast-deep-equal]{@link https://github.com/epoberezkin/fast-deep-equal/} 177 | * written by Evgeny Poberezkin 178 | */ 179 | static isEqual(a: unknown, b: unknown): boolean { 180 | if (a === b) return true; 181 | 182 | if (a && b && typeof a == "object" && typeof b == "object") { 183 | if (a.constructor !== b.constructor) return false; 184 | 185 | let length, i; 186 | if (Array.isArray(a)) { 187 | length = a.length; 188 | if (length != (b as Array).length) return false; 189 | for (i = length; i-- !== 0;) { 190 | if (!this.isEqual(a[i], (b as Array)[i])) return false; 191 | } 192 | return true; 193 | } 194 | 195 | if (a.constructor === RegExp) { 196 | return (a as RegExp).source === (b as RegExp).source && 197 | (a as RegExp).flags === (b as RegExp).flags; 198 | } 199 | if (a.valueOf !== Object.prototype.valueOf) { 200 | return a.valueOf() === b.valueOf(); 201 | } 202 | if (a.toString !== Object.prototype.toString) { 203 | return a.toString() === b.toString(); 204 | } 205 | 206 | const keys = Object.keys(a); 207 | length = keys.length; 208 | if (length !== Object.keys(b).length) return false; 209 | 210 | for (i = length; i-- !== 0;) { 211 | if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; 212 | } 213 | 214 | for (i = length; i-- !== 0;) { 215 | const key = keys[i]; 216 | if (!this.isEqual(a[key as keyof typeof a], b[key as keyof typeof b])) { 217 | return false; 218 | } 219 | } 220 | 221 | return true; 222 | } 223 | 224 | // true if both NaN, false otherwise 225 | return a !== a && b !== b; 226 | } 227 | 228 | /** 229 | * Sets up Proxy objects on an observable to monitor changes. 230 | * @param target the target object to watch for the new Proxy 231 | * @param path the string path to the object in the observable 232 | * @param observable the observable to which the proxy should belong 233 | * @param processed an array of already processed objects 234 | * @returns a new Proxy object 235 | */ 236 | static getProxy( 237 | // deno-lint-ignore no-explicit-any 238 | target: any, 239 | path: string, 240 | observable: _Observable, 241 | processed: Array, 242 | ) { 243 | let proxy; 244 | // if the target already has a proxy 245 | if (target[sObservable]) { 246 | proxy = target; 247 | proxy[sObservable] = observable; 248 | proxy[sPath] = path; 249 | } else { 250 | const handler = Array.isArray(target) ? this.arrayHandler : this.handler; 251 | proxy = new Proxy(target, handler); 252 | Reflect.defineProperty(proxy, sObservable, { 253 | value: observable, 254 | writable: true, 255 | }); 256 | Reflect.defineProperty(proxy, sPath, { 257 | value: path, 258 | writable: true, 259 | }); 260 | if (target === observable) { 261 | target[sObservable] = proxy; 262 | observable = proxy; 263 | } 264 | } 265 | 266 | this.setProxies(target, path, observable, processed); 267 | return proxy; 268 | } 269 | 270 | static setProxies( 271 | target: T, 272 | path: string, 273 | observable: _Observable, 274 | processed: Array, 275 | ): void { 276 | const keys = Object.keys(target) as Array; 277 | for (const key of keys) { 278 | if ( 279 | _Observable.isObjectObservable(target[key]) && 280 | !processed.includes(target[key]) 281 | ) { 282 | processed.push(target[key]); 283 | target[key] = this.getProxy( 284 | target[key], 285 | `${path}.${key as string}`, 286 | observable, 287 | processed, 288 | ); 289 | } 290 | } 291 | } 292 | 293 | static arrayGetTrap( 294 | target: T, 295 | property: keyof T, 296 | receiver: ObservedValue, 297 | ): unknown { 298 | if (typeof property === "string" && watchedArrayMethods.has(property)) { 299 | return (...args: unknown[]) => { 300 | // deno-lint-ignore no-explicit-any 301 | const value = (target[property] as any)(...args); 302 | _Observable.setProxies(target, receiver[sPath], receiver[sObservable], [ 303 | receiver, 304 | ]); 305 | switch (property) { 306 | case "push": 307 | case "unshift": 308 | target[sObservable].dispatchEvent( 309 | new ChangeEvent(target[sPath], ChangeType.Add, undefined, args), 310 | ); 311 | break; 312 | case "shift": 313 | case "pop": 314 | target[sObservable].dispatchEvent( 315 | new ChangeEvent( 316 | target[sPath], 317 | ChangeType.Remove, 318 | undefined, 319 | value, 320 | ), 321 | ); 322 | break; 323 | case "splice": 324 | target[sObservable].dispatchEvent( 325 | new ChangeEvent( 326 | target[sPath], 327 | ChangeType.Remove, 328 | undefined, 329 | value, 330 | ), 331 | ); 332 | if (args.length > 2) { 333 | target[sObservable].dispatchEvent( 334 | new ChangeEvent( 335 | target[sPath], 336 | ChangeType.Add, 337 | undefined, 338 | args.slice(2), 339 | ), 340 | ); 341 | } 342 | break; 343 | case "sort": 344 | target[sObservable].dispatchEvent( 345 | new ChangeEvent(target[sPath], ChangeType.Sort), 346 | ); 347 | break; 348 | } 349 | return value; 350 | }; 351 | } 352 | return target[property]; 353 | } 354 | 355 | static setTrap( 356 | target: T, 357 | property: keyof T, 358 | value: unknown, 359 | receiver: unknown, 360 | ): boolean { 361 | // do not track symbols or non-enumerable properties 362 | if ( 363 | typeof property === "symbol" || 364 | (Reflect.has(target, property) && 365 | !Object.prototype.propertyIsEnumerable.call(target, property)) 366 | ) { 367 | Reflect.set(target, property, value, receiver); 368 | return true; 369 | } 370 | if (_Observable.isEqual(target[property], value)) return true; 371 | const path: string = target[sPath]; 372 | const observable: _Observable = target[sObservable]; 373 | const previous = target[property]; 374 | const propertyPath = `${path}.${property as string}`; 375 | target[property] = _Observable.isObjectObservable(value) 376 | ? _Observable.getProxy(value, propertyPath, observable, [value]) 377 | : value; 378 | observable.dispatchEvent( 379 | new ChangeEvent(propertyPath, ChangeType.Set, previous), 380 | ); 381 | return true; 382 | } 383 | 384 | static deletePropertyTrap( 385 | target: T, 386 | property: keyof T, 387 | ) { 388 | if (!Reflect.has(target, property)) return true; 389 | if ( 390 | typeof property === "symbol" || 391 | !Object.prototype.propertyIsEnumerable.call(target, property) 392 | ) { 393 | delete target[property]; 394 | return true; 395 | } 396 | const path = target[sPath]; 397 | const observable = target[sObservable]; 398 | const previous = target[property]; 399 | const propertyPath = `${path}.${property as string}`; 400 | delete target[property]; 401 | observable.dispatchEvent( 402 | new ChangeEvent(propertyPath, ChangeType.Delete, previous), 403 | ); 404 | return true; 405 | } 406 | } 407 | 408 | export type Observable = _Observable & K; 409 | 410 | export const Observable = _Observable as { 411 | new (properties: T): Observable; 412 | }; 413 | -------------------------------------------------------------------------------- /observer.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveElement } from "./deps.ts"; 2 | import { ObservedValue, sObservable, sPath } from "./observable.ts"; 3 | import type { ChangeEvent } from "./observable.ts"; 4 | import { Constructor } from "./constructor.ts"; 5 | 6 | const sHandlers = Symbol.for("c-handlers"); 7 | const sObservables = Symbol.for("c-observables"); 8 | 9 | export function ObserverElement>( 10 | Base: U, 11 | ) { 12 | return class ObserverElement extends Base { 13 | [sObservables] = new Map(); 14 | [sHandlers] = new Map void>(); 15 | declare static observables: Array; 16 | 17 | // deno-lint-ignore no-explicit-any 18 | constructor(...args: any[]) { 19 | super(...args); 20 | const { observables } = this 21 | .constructor as typeof ObserverElement; 22 | if (observables) { 23 | for (const property of observables) { 24 | this.setObservable(property); 25 | } 26 | } 27 | } 28 | 29 | onObservableChange( 30 | property: PropertyKey, 31 | event: ChangeEvent, 32 | ): void { 33 | const pathPrefix: string = 34 | (this as unknown as { [key: PropertyKey]: ObservedValue })[property][ 35 | sPath 36 | ]; 37 | if (event.path.startsWith(pathPrefix)) { 38 | const elementPath = `${property as string}${ 39 | event.path.slice(pathPrefix.length) 40 | }`; 41 | this.requestUpdate(elementPath, event); 42 | } 43 | } 44 | 45 | setObservable( 46 | property: PropertyKey, 47 | ) { 48 | const value = 49 | (this as unknown as { [key: PropertyKey]: ObservedValue })[property]; 50 | Object.defineProperty(this, property, { 51 | get(this: ObserverElement): ObservedValue | undefined { 52 | return this[sObservables].get(property); 53 | }, 54 | set(this: ObserverElement, value: ObservedValue) { 55 | const oldValue = this[sObservables].get(property); 56 | if (oldValue === value) return; 57 | if (!this[sHandlers].has(property)) { 58 | this[sHandlers].set( 59 | property, 60 | this.onObservableChange.bind(this, property), 61 | ); 62 | } 63 | const handler = this[sHandlers].get(property)!; 64 | if (oldValue && oldValue[sObservable]) { 65 | oldValue[sObservable].removeEventListener("change", handler); 66 | } 67 | if (value && value[sObservable]) { 68 | value[sObservable].addEventListener("change", handler); 69 | } 70 | this[sObservables].set(property, value); 71 | this.requestUpdate(property, oldValue); 72 | }, 73 | configurable: true, 74 | enumerable: true, 75 | }); 76 | if (value) { 77 | (this as unknown as { [key: PropertyKey]: ObservedValue })[property] = 78 | value; 79 | } 80 | } 81 | 82 | disconnectedCallback(): void { 83 | super.disconnectedCallback(); 84 | for (const [property, observable] of this[sObservables].entries()) { 85 | observable[sObservable].removeEventListener( 86 | "change", 87 | this[sHandlers].get(property)!, 88 | ); 89 | } 90 | } 91 | } as U; 92 | } 93 | 94 | export function observer() { 95 | return ObserverElement; 96 | } 97 | 98 | export function observe() { 99 | return function ( 100 | // deno-lint-ignore no-explicit-any 101 | target: any, 102 | property: PropertyKey, 103 | ) { 104 | const observables = target.constructor.observables || []; 105 | observables.push(property); 106 | target.constructor.observables = observables; 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /repository.ts: -------------------------------------------------------------------------------- 1 | import type { Result } from "./result.ts"; 2 | 3 | /** 4 | * Enforces id attribute on a type. Id attribute name can be specified 5 | * as the second type argument, uses `_id` by default. 6 | */ 7 | export type Entity< 8 | T extends { [K in ID]?: unknown }, 9 | ID extends string = "_id", 10 | > = 11 | & T 12 | & { [K in ID]: Exclude }; 13 | 14 | /** 15 | * The general interface of Repositories. 16 | */ 17 | export interface Repository { 18 | /** 19 | * Checks if object exists in the repository. 20 | * 21 | * @param id id of the object 22 | */ 23 | has(id: unknown): Promise>; 24 | 25 | /** 26 | * Retrieves multiple objects from the repository. 27 | * 28 | * @param args search parameters 29 | */ 30 | list(...args: Array): Promise, unknown>>; 31 | 32 | /** 33 | * Adds a new object to the repository. 34 | * 35 | * @param value the object to be added 36 | */ 37 | create(value: T): Promise>; 38 | 39 | /** 40 | * Gets a single object from the repository. 41 | * 42 | * @param id id of the object 43 | */ 44 | read(id: unknown): Promise>; 45 | 46 | /** 47 | * Updates an object stored in the repository. 48 | * 49 | * @param args update parameters 50 | */ 51 | update(...args: Array): Promise>; 52 | 53 | /** 54 | * Removes an object from the repository. 55 | * 56 | * @param id id of the object 57 | */ 58 | delete(id: unknown): Promise>; 59 | 60 | /** 61 | * Encode object for storage. 62 | * 63 | * @param entity object to serialize 64 | */ 65 | serialize(entity: Partial): unknown; 66 | 67 | /** 68 | * Create an object from a stored value. 69 | * 70 | * @param value value to deserialize 71 | */ 72 | deserialize(value: unknown): T; 73 | } 74 | -------------------------------------------------------------------------------- /rest-repository.ts: -------------------------------------------------------------------------------- 1 | import type { Constructor } from "./constructor.ts"; 2 | import type { Repository } from "./repository.ts"; 3 | import { Result } from "./result.ts"; 4 | 5 | /** 6 | * Encapsulates the logic required to access data sources 7 | * exposed as RESTful APIs. 8 | */ 9 | export class RESTRepository implements Repository { 10 | static init: Partial = { 11 | method: "GET", 12 | headers: { 13 | "Content-Type": "application/json", 14 | }, 15 | }; 16 | 17 | constructor( 18 | public Entity: Constructor, 19 | public url: string, 20 | public idProperty: string = "_id", 21 | ) {} 22 | 23 | has(id: string): Promise> { 24 | return Promise.resolve(Result.ok(!!id)); 25 | } 26 | 27 | async query( 28 | search?: Record, 29 | path = "", 30 | ): Promise> { 31 | let { url } = this; 32 | if (path) url += path; 33 | if (search) url += `?${new URLSearchParams(search).toString()}`; 34 | return await (this.constructor as typeof RESTRepository).fetch(url); 35 | } 36 | 37 | async command( 38 | body: unknown, 39 | path = "", 40 | ): Promise> { 41 | let { url } = this; 42 | if (path) url += path; 43 | return await (this.constructor as typeof RESTRepository).fetch(url, { 44 | method: "POST", 45 | body: JSON.stringify(body), 46 | }); 47 | } 48 | 49 | async list( 50 | search?: Record, 51 | ): Promise, undefined | Response | TypeError>> { 52 | const result = await this.query>(search); 53 | if (!result.ok) return result; 54 | if (result.value === undefined) return Result.fail(undefined); 55 | return Result.ok(result.value.map((i) => this.deserialize(i))); 56 | } 57 | 58 | create(value: T): Promise> { 59 | return (this.constructor as typeof RESTRepository).fetch( 60 | `${this.url}`, 61 | { method: "POST", body: this.serialize(value) as string }, 62 | ); 63 | } 64 | 65 | async read(id: string): Promise> { 66 | const result = await (this.constructor as typeof RESTRepository).fetch( 67 | `${this.url}/${id}`, 68 | ); 69 | if (!result.ok) return result; 70 | return Result.ok(this.deserialize(result.value)); 71 | } 72 | 73 | update( 74 | id: string, 75 | updates: Partial, 76 | ): Promise> { 77 | return (this.constructor as typeof RESTRepository).fetch( 78 | `${this.url}/${id}`, 79 | { method: "PUT", body: this.serialize(updates) as string }, 80 | ); 81 | } 82 | 83 | delete(id: string): Promise> { 84 | return (this.constructor as typeof RESTRepository).fetch( 85 | `${this.url}/${id}`, 86 | { method: "DELETE" }, 87 | ); 88 | } 89 | 90 | serialize(entity: Partial): unknown { 91 | return JSON.stringify(entity); 92 | } 93 | 94 | deserialize(value: unknown): T { 95 | return new this.Entity(value); 96 | } 97 | 98 | async save(value: T): Promise> { 99 | const id = value[this.idProperty as keyof T] as unknown as string; 100 | const exists = await this.has(id); 101 | if (exists.value) { 102 | return this.update(id, value); 103 | } 104 | return this.create(value); 105 | } 106 | 107 | static async fetch( 108 | url: string, 109 | init: Partial = {}, 110 | ): Promise> { 111 | try { 112 | const response = await globalThis.fetch(url, { ...this.init, ...init }); 113 | if (response.ok || response.status === 304) { 114 | const contentType = response.headers.get("Content-Type"); 115 | if (contentType) { 116 | const body: T = contentType.includes("application/json") 117 | ? await response.json() 118 | : await response.arrayBuffer(); 119 | return Result.ok(body); 120 | } 121 | return Result.ok(undefined); 122 | } else { 123 | return Result.fail(response); 124 | } 125 | } catch (e) { 126 | return Result.fail(e); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /result.ts: -------------------------------------------------------------------------------- 1 | export class Ok { 2 | readonly ok = true; 3 | constructor(readonly value: T) {} 4 | } 5 | 6 | export class Err<_, E> { 7 | readonly ok = false; 8 | constructor(readonly value: E) {} 9 | } 10 | 11 | export type Result = Ok | Err; 12 | 13 | export const Result = { 14 | ok: (value: T): Ok => new Ok(value), 15 | fail: (err: E): Err => new Err(err), 16 | }; 17 | -------------------------------------------------------------------------------- /tests/bond-directive_test.ts: -------------------------------------------------------------------------------- 1 | import "./dom.ts"; 2 | import { assertEquals, assertThrows, spy } from "./test_deps.ts"; 3 | import type { EventPart } from "../deps.ts"; 4 | import type { BondDirective as BondType } from "../bond-directive.ts"; 5 | 6 | const { test } = Deno; 7 | const { LitElement } = await import("../deps.ts"); 8 | const { BondDirective } = await import("../bond-directive.ts"); 9 | 10 | class ComponentClass extends LitElement { 11 | nested?: Record; 12 | } 13 | 14 | customElements.define("c-component", ComponentClass); 15 | 16 | type Recipient = { 17 | a: number; 18 | b: string; 19 | c: Array; 20 | }; 21 | 22 | const bondContext = ( 23 | callback: ( 24 | recipient: Recipient, 25 | directive: BondType, 26 | ) => void, 27 | ) => { 28 | return () => 29 | callback( 30 | { a: 1, b: "", c: [] }, 31 | new BondDirective({ 32 | type: 5, 33 | name: "", 34 | tagName: "a", 35 | }), 36 | ); 37 | }; 38 | 39 | test("[BondDirective#constructor] can only be attached to an event expression", () => { 40 | assertThrows(() => { 41 | new BondDirective({ type: 1, name: "", tagName: "div" }); 42 | }, Error); 43 | assertThrows(() => { 44 | new BondDirective({ type: 2 }); 45 | }, Error); 46 | assertThrows(() => { 47 | new BondDirective({ type: 3, name: "", tagName: "div" }); 48 | }, Error); 49 | assertThrows(() => { 50 | new BondDirective({ type: 4, name: "", tagName: "div" }); 51 | }, Error); 52 | assertEquals( 53 | new BondDirective({ type: 5, name: "", tagName: "div" }) instanceof 54 | BondDirective, 55 | true, 56 | ); 57 | }); 58 | 59 | test( 60 | "[BondDirective#handler] sets a given constant", 61 | bondContext((recipient, directive) => { 62 | const options = { to: recipient, key: "a", value: 10 } as const; 63 | directive.update({} as unknown as EventPart, [options]); 64 | directive.handler(new Event("click")); 65 | assertEquals(recipient.a, 10); 66 | }), 67 | ); 68 | 69 | test( 70 | "[BondDirective#handler] prevents default event behavior if `prevent:true`", 71 | bondContext((recipient, directive) => { 72 | const options = { 73 | to: recipient, 74 | key: "a", 75 | value: 10, 76 | prevent: true, 77 | } as const; 78 | directive.update({} as unknown as EventPart, [options]); 79 | const event = new Event("click"); 80 | const preventSpy = spy(); 81 | event.preventDefault = preventSpy; 82 | directive.handler(event); 83 | assertEquals(preventSpy.calls.length, 1); 84 | assertEquals(recipient.a, 10); 85 | }), 86 | ); 87 | 88 | test( 89 | "[BondDirective#handler] sets value from an attribute", 90 | bondContext((recipient, directive) => { 91 | const host = document.createElement("div"); 92 | host.setAttribute("abc", "xyz"); 93 | const options = { to: recipient, key: "b", attribute: "abc" } as const; 94 | directive.update( 95 | { element: host, options: { host } } as unknown as EventPart, 96 | [options], 97 | ); 98 | directive.handler(new Event("click")); 99 | assertEquals(recipient.b, "xyz"); 100 | }), 101 | ); 102 | 103 | test( 104 | "[BondDirective#handler] sets value from a property", 105 | bondContext((recipient, directive) => { 106 | const host = document.createElement("div"); 107 | const options = { to: recipient, key: "b", property: "nodeName" } as const; 108 | directive.update( 109 | { element: host, options: { host } } as unknown as EventPart, 110 | [options], 111 | ); 112 | directive.handler(new Event("click")); 113 | assertEquals(recipient.b, "DIV"); 114 | }), 115 | ); 116 | 117 | test( 118 | "[BondDirective#handler] does not set value if validation fails", 119 | bondContext((recipient, directive) => { 120 | const host = document.createElement("div"); 121 | host.setAttribute("abc", "xyz"); 122 | const options = { 123 | to: recipient, 124 | key: "b", 125 | attribute: "abc", 126 | validate: () => false, 127 | } as const; 128 | directive.update( 129 | { element: host, options: { host } } as unknown as EventPart, 130 | [options], 131 | ); 132 | directive.handler(new Event("click")); 133 | assertEquals(recipient.b, ""); 134 | }), 135 | ); 136 | 137 | test( 138 | "[BondDirective#handler] parses value if `parse` function provided", 139 | bondContext((recipient, directive) => { 140 | const host = document.createElement("div"); 141 | host.setAttribute("abc", "100"); 142 | const options = { 143 | to: recipient, 144 | key: "a", 145 | attribute: "abc", 146 | parse: parseInt, 147 | } as const; 148 | directive.update( 149 | { element: host, options: { host } } as unknown as EventPart, 150 | [options], 151 | ); 152 | directive.handler(new Event("click")); 153 | assertEquals(recipient.a, 100); 154 | }), 155 | ); 156 | -------------------------------------------------------------------------------- /tests/dom.ts: -------------------------------------------------------------------------------- 1 | import { parseHTML } from "./test_deps.ts"; 2 | 3 | const { document, HTMLElement, customElements, HTMLIFrameElement } = parseHTML( 4 | ``, 5 | ); 6 | globalThis.document = document; 7 | globalThis.HTMLElement = HTMLElement; 8 | globalThis.customElements = customElements; 9 | globalThis.HTMLIFrameElement = HTMLIFrameElement; 10 | -------------------------------------------------------------------------------- /tests/local-repository_test.ts: -------------------------------------------------------------------------------- 1 | import "https://deno.land/x/indexeddb@1.3.5/polyfill_memory.ts"; 2 | import { assertEquals } from "./test_deps.ts"; 3 | import { LocalRepository } from "../local-repository.ts"; 4 | 5 | class Character { 6 | name = ""; 7 | planet = ""; 8 | constructor(init: Partial) { 9 | Object.assign(this, init); 10 | } 11 | } 12 | 13 | const DATA: Array = [ 14 | new Character({ name: "Arthur Dent", planet: "Earth" }), 15 | new Character({ name: "Ford Prefect", planet: "Betelgeuse Five" }), 16 | new Character({ name: "Zaphod Beeblebrox", planet: "Betelgeuse Five" }), 17 | ]; 18 | 19 | await new Promise((resolve, reject) => { 20 | const request = globalThis.indexedDB.open("testDb", 1); 21 | request.onupgradeneeded = () => { 22 | const db = request.result; 23 | const store = db.createObjectStore("users", { 24 | keyPath: "name", 25 | }); 26 | store.createIndex("name", "name", { unique: true }); 27 | }; 28 | request.onsuccess = async () => { 29 | const db = request.result; 30 | await LocalRepository.promisify( 31 | db.transaction("users", "readwrite").objectStore("users").add(DATA[0]), 32 | ); 33 | await LocalRepository.promisify( 34 | db.transaction("users", "readwrite").objectStore("users").add(DATA[1]), 35 | ); 36 | resolve(); 37 | }; 38 | request.onerror = () => { 39 | reject(); 40 | }; 41 | }); 42 | 43 | async function getDB() { 44 | const result = await LocalRepository.promisify( 45 | globalThis.indexedDB.open("testDb", 1), 46 | ); 47 | return result.value as IDBDatabase; 48 | } 49 | 50 | Deno.test({ 51 | name: "[LocalRepository#constructor] create a repository", 52 | fn: async () => { 53 | const db = await getDB(); 54 | const repository = new LocalRepository(Character, db, "users", "name"); 55 | assertEquals(repository.db.name, "testDb"); 56 | db.close(); 57 | }, 58 | sanitizeOps: false, 59 | sanitizeResources: false, 60 | }); 61 | 62 | Deno.test({ 63 | name: "[LocalRepository#has] checks if an object exists in the repository", 64 | fn: async () => { 65 | const db = await getDB(); 66 | const repository = new LocalRepository(Character, db, "users", "name"); 67 | let result = await repository.has("Arthur Dent"); 68 | assertEquals(result.value, true); 69 | result = await repository.has("Arthur"); 70 | assertEquals(result.value, false); 71 | result = await repository.has(""); 72 | assertEquals(result.value, false); 73 | }, 74 | sanitizeOps: false, 75 | sanitizeResources: false, 76 | }); 77 | 78 | Deno.test({ 79 | name: "[LocalRepository#list] returns a list of all objects with a given key", 80 | fn: async () => { 81 | const db = await getDB(); 82 | const repository = new LocalRepository(Character, db, "users", "name"); 83 | const result = await repository.list("Arthur Dent"); 84 | assertEquals(result.value, [DATA[0]]); 85 | }, 86 | sanitizeOps: false, 87 | sanitizeResources: false, 88 | }); 89 | 90 | Deno.test({ 91 | name: "[LocalRepository#list] returns a list of all objects for given keys", 92 | fn: async () => { 93 | const db = await getDB(); 94 | const repository = new LocalRepository(Character, db, "users", "name"); 95 | const result = await repository.list("Arthur Dent", "Ford Prefect"); 96 | assertEquals(result.value, [DATA[0], DATA[1]]); 97 | }, 98 | sanitizeOps: false, 99 | sanitizeResources: false, 100 | }); 101 | 102 | Deno.test({ 103 | name: "[LocalRepository#create] adds an object to the repository", 104 | fn: async () => { 105 | const db = await getDB(); 106 | const repository = new LocalRepository(Character, db, "users", "name"); 107 | assertEquals((await repository.has("Zaphod Beeblebrox")).value, false); 108 | await repository.create(DATA[2]); 109 | assertEquals((await repository.has("Zaphod Beeblebrox")).value, true); 110 | await repository.delete("Zaphod Beeblebrox"); 111 | }, 112 | sanitizeOps: false, 113 | sanitizeResources: false, 114 | }); 115 | 116 | Deno.test({ 117 | name: "[LocalRepository#read] get an object from the repository", 118 | fn: async () => { 119 | const db = await getDB(); 120 | const repository = new LocalRepository(Character, db, "users", "name"); 121 | let request = await repository.read("Arthur Dent"); 122 | assertEquals(request.ok, true); 123 | assertEquals(request.value, DATA[0]); 124 | request = await repository.read("Ford"); 125 | assertEquals(request.ok, false); 126 | assertEquals(request.value, undefined); 127 | }, 128 | sanitizeOps: false, 129 | sanitizeResources: false, 130 | }); 131 | 132 | Deno.test({ 133 | name: "[LocalRepository#update] updates an object in the repository", 134 | fn: async () => { 135 | const db = await getDB(); 136 | const repository = new LocalRepository(Character, db, "users", "name"); 137 | assertEquals((await repository.read("Arthur Dent")).value, DATA[0]); 138 | const updated = new Character({ 139 | name: "Arthur Dent", 140 | planet: "Non-existant", 141 | }); 142 | await repository.update("Arthur Dent", updated); 143 | assertEquals((await repository.read("Arthur Dent")).value, updated); 144 | await repository.update("Arthur Dent", DATA[0]); 145 | }, 146 | sanitizeOps: false, 147 | sanitizeResources: false, 148 | }); 149 | 150 | Deno.test({ 151 | name: "[LocalRepository#delete] deletes an object from the repository", 152 | fn: async () => { 153 | const db = await getDB(); 154 | const repository = new LocalRepository(Character, db, "users", "name"); 155 | assertEquals((await repository.has("Zaphod Beeblebrox")).value, false); 156 | await repository.create({ 157 | name: "Zaphod Beeblebrox", 158 | planet: "Betelgeuse Five", 159 | }); 160 | assertEquals((await repository.has("Zaphod Beeblebrox")).value, true); 161 | await repository.delete("Zaphod Beeblebrox"); 162 | assertEquals((await repository.has("Zaphod Beeblebrox")).value, false); 163 | }, 164 | sanitizeOps: false, 165 | sanitizeResources: false, 166 | }); 167 | -------------------------------------------------------------------------------- /tests/mod_test.ts: -------------------------------------------------------------------------------- 1 | import "./dom.ts"; 2 | import { assert, assertEquals } from "./test_deps.ts"; 3 | 4 | const mod = await import("../mod.ts"); 5 | 6 | Deno.test("Public API Assertions", () => { 7 | assert(mod != null); 8 | assertEquals(typeof mod.ChangeEvent, "function"); 9 | assertEquals(typeof mod.ChangeType, "object"); 10 | assertEquals(typeof mod.bond, "function"); 11 | assertEquals(typeof mod.Observable, "function"); 12 | assertEquals(typeof mod.ObserverElement, "function"); 13 | assertEquals(typeof mod.observer, "function"); 14 | assertEquals(typeof mod.observe, "function"); 15 | assertEquals(typeof mod.Result, "object"); 16 | assertEquals(typeof mod.RESTRepository, "function"); 17 | assertEquals(typeof mod.LocalRepository, "function"); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/observable_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals, equal, Spy, spy } from "./test_deps.ts"; 2 | import { 3 | _Observable, 4 | ChangeEvent, 5 | ChangeType, 6 | Observable, 7 | } from "../observable.ts"; 8 | 9 | const { test } = Deno; 10 | 11 | interface IData { 12 | answer: number; 13 | question?: string; 14 | object?: { name: string; heads?: number }; 15 | array?: Array; 16 | re?: RegExp; 17 | anyMap?: Map; 18 | anySet?: Set; 19 | } 20 | 21 | class DataObservable extends Observable {} 22 | 23 | function toPlainObject(object: unknown): object { 24 | return JSON.parse(JSON.stringify(object)); 25 | } 26 | 27 | const observableContext = ( 28 | callback: (observable: DataObservable, changeSpy: Spy) => void, 29 | ) => { 30 | return () => { 31 | const observable = new DataObservable({ 32 | answer: 42, 33 | question: "", 34 | object: { name: "Zaphod", heads: 1 }, 35 | array: undefined, 36 | }); 37 | const changeSpy = spy(); 38 | observable.addEventListener("change", changeSpy); 39 | callback(observable, changeSpy); 40 | }; 41 | }; 42 | 43 | const assertChange = ( 44 | event: unknown, 45 | path: string, 46 | kind: ChangeType, 47 | previous?: unknown, 48 | elements?: unknown, 49 | ) => { 50 | const isEvent = event instanceof ChangeEvent; 51 | if ( 52 | !isEvent || event.path !== path || event.kind !== kind || 53 | !equal(event.previous, previous) || 54 | !equal(event.elements, elements) 55 | ) { 56 | throw new Error("Events don't match"); 57 | } 58 | }; 59 | 60 | test("[Observable#constructor] creates an observable", () => { 61 | const observable = new Observable({}); 62 | assertEquals(observable instanceof Observable, true); 63 | assertEquals(toPlainObject(observable), {}); 64 | }); 65 | 66 | test( 67 | "[Observable@change] emits `change` event", 68 | observableContext((observable, changeSpy) => { 69 | observable.answer = 1; 70 | assertEquals(changeSpy.calls.length, 1); 71 | assertChange( 72 | changeSpy.calls[0].args[0], 73 | ".answer", 74 | ChangeType.Set, 75 | 42, 76 | ); 77 | }), 78 | ); 79 | 80 | test("[Observable@change] handles setter", () => { 81 | class ObservableSetters 82 | extends Observable<{ answer?: number; _id?: number }> { 83 | set setAnswer(value: number) { 84 | this.answer = value; 85 | } 86 | } 87 | 88 | const observableWithSetter = new ObservableSetters({ 89 | answer: 42, 90 | _id: 10, 91 | }); 92 | assertEquals(observableWithSetter._id, 10); 93 | const changeSpy = spy(); 94 | observableWithSetter.addEventListener("change", changeSpy); 95 | observableWithSetter.setAnswer = 45; 96 | assertEquals(changeSpy.calls.length, 1); 97 | assertChange(changeSpy.calls[0].args[0], ".answer", ChangeType.Set, 42); 98 | }); 99 | 100 | test( 101 | "[Observable@change] reacts to changes in nested objects", 102 | observableContext((observable, changeSpy) => { 103 | observable.object!.name = "Ford"; 104 | observable.assign({ array: [1] }); 105 | 106 | assertEquals(changeSpy.calls.length, 2); 107 | assertChange( 108 | changeSpy.calls[0].args[0], 109 | ".object.name", 110 | ChangeType.Set, 111 | "Zaphod", 112 | ); 113 | assertChange(changeSpy.calls[1].args[0], ".array", ChangeType.Set); 114 | }), 115 | ); 116 | 117 | test( 118 | "[Observable@change] reacts to changes in nested arrays", 119 | observableContext((observable, changeSpy) => { 120 | observable.array = [0]; 121 | observable.array[1] = 2; 122 | assertEquals(changeSpy.calls.length, 2); 123 | assertChange(changeSpy.calls[0].args[0], ".array", ChangeType.Set); 124 | assertChange(changeSpy.calls[1].args[0], ".array.1", ChangeType.Set); 125 | }), 126 | ); 127 | 128 | test("[Observable@change] handles cyclic self-references", () => { 129 | interface AB { 130 | a?: number; 131 | b?: number; 132 | c?: AB; 133 | } 134 | const ab: AB = { a: 1, b: 1 }; 135 | const abObservable = new Observable(ab); 136 | assertEquals(abObservable.a, 1); 137 | abObservable.c = abObservable; 138 | const changeSpy = spy(); 139 | abObservable.addEventListener("change", changeSpy); 140 | abObservable.a = 2; 141 | abObservable.c!.a = 3; 142 | assertEquals(abObservable.a, 3); 143 | assertEquals(changeSpy.calls.length, 2); 144 | assertChange(changeSpy.calls[0].args[0], ".a", ChangeType.Set, 1); 145 | assertChange(changeSpy.calls[1].args[0], ".a", ChangeType.Set, 2); 146 | }); 147 | 148 | test("[Observable@change] handles moving objects within", () => { 149 | interface AB { 150 | a?: number; 151 | b?: number; 152 | c?: { d: number }; 153 | d?: { d: number }; 154 | } 155 | const ab: AB = { a: 1, b: 1 }; 156 | const abObservable = new Observable(ab); 157 | assertEquals(abObservable.a, 1); 158 | abObservable.c = { d: 1 }; 159 | abObservable.d = abObservable.c; 160 | const changeSpy = spy(); 161 | abObservable.addEventListener("change", changeSpy); 162 | abObservable.c.d = 2; 163 | assertEquals(changeSpy.calls.length, 1); 164 | assertChange(changeSpy.calls[0].args[0], ".d.d", ChangeType.Set, 1); 165 | }); 166 | 167 | test( 168 | "[Observable@change] allows usage of any type of property", 169 | observableContext((observable, changeSpy) => { 170 | observable.re = new RegExp("abc"); 171 | assertChange(changeSpy.calls[0].args[0], ".re", ChangeType.Set); 172 | }), 173 | ); 174 | 175 | test( 176 | "[Observable@change] does not react to changes inside built-in instances of classes that are not Object or Array", 177 | observableContext((observable, changeSpy) => { 178 | observable.anyMap = new Map(); 179 | observable.anySet = new Set(); 180 | observable.anyMap.set("1", 1); 181 | observable.anySet.add(1); 182 | assertEquals(observable.anyMap.has("1"), true); 183 | assertEquals(observable.anySet.has(1), true); 184 | assertEquals(changeSpy.calls.length, 2); 185 | }), 186 | ); 187 | 188 | test( 189 | "[Observable@change] does not react to changing symbols", 190 | () => { 191 | const s = Symbol(); 192 | interface A { 193 | [s]: number; 194 | } 195 | const observableWithSymbol = new Observable({ [s]: 1 }); 196 | const changeSpy = spy(); 197 | observableWithSymbol.addEventListener("change", changeSpy); 198 | observableWithSymbol[s] = 10; 199 | assertEquals(observableWithSymbol[s], 10); 200 | assertEquals(changeSpy.calls.length, 0); 201 | }, 202 | ); 203 | 204 | test( 205 | "[Observable@change] does not react to changing non-enumerable properties", 206 | observableContext((observable, changeSpy) => { 207 | Object.defineProperty(observable, "abc", { 208 | value: 1, 209 | enumerable: false, 210 | writable: true, 211 | }); 212 | // deno-lint-ignore no-explicit-any 213 | (observable as any).abc = 2; 214 | assertEquals(changeSpy.calls.length, 0); 215 | }), 216 | ); 217 | 218 | test( 219 | "[Observable@change] reacts to deleting properties", 220 | observableContext((observable, changeSpy) => { 221 | delete observable.question; 222 | assertChange( 223 | changeSpy.calls[0].args[0], 224 | ".question", 225 | ChangeType.Delete, 226 | "", 227 | ); 228 | }), 229 | ); 230 | 231 | test( 232 | "[Observable@change] does not react to deleting non-existing properties", 233 | observableContext((observable, changeSpy) => { 234 | // deno-lint-ignore no-explicit-any 235 | delete (observable as any).nonexisting; 236 | assertEquals(changeSpy.calls.length, 0); 237 | }), 238 | ); 239 | 240 | test( 241 | "[Observable@change] does not react to deleting properties set up with symbols", 242 | () => { 243 | const s = Symbol(); 244 | interface A { 245 | [s]?: number; 246 | } 247 | const observableWithSymbol = new Observable({ [s]: 1 }); 248 | const changeSpy = spy(); 249 | observableWithSymbol.addEventListener("change", changeSpy); 250 | delete observableWithSymbol[s]; 251 | assertEquals(changeSpy.calls.length, 0); 252 | }, 253 | ); 254 | 255 | test( 256 | "[Observable@change] does not react to deleting non-enumerable properties", 257 | observableContext((observable, changeSpy) => { 258 | Object.defineProperty(observable, "abc", { 259 | value: 1, 260 | enumerable: false, 261 | writable: true, 262 | configurable: true, 263 | }); 264 | // deno-lint-ignore no-explicit-any 265 | delete (observable as any).abc; 266 | assertEquals(changeSpy.calls.length, 0); 267 | }), 268 | ); 269 | 270 | test( 271 | "[Observable@change] reacts to adding elements to nested arrays", 272 | observableContext((observable, changeSpy) => { 273 | observable.array = [1, 2, 3]; 274 | observable.array.push(4, 5, 6); 275 | observable.array.unshift(0); 276 | assertEquals(changeSpy.calls.length, 3); 277 | assertChange( 278 | changeSpy.calls[1].args[0], 279 | ".array", 280 | ChangeType.Add, 281 | undefined, 282 | [4, 5, 6], 283 | ); 284 | assertChange( 285 | changeSpy.calls[2].args[0], 286 | ".array", 287 | ChangeType.Add, 288 | undefined, 289 | [0], 290 | ); 291 | }), 292 | ); 293 | 294 | test( 295 | "[Observable@change] reacts to changes in objects nested in arrays", 296 | observableContext((observable, changeSpy) => { 297 | observable.array = []; 298 | observable.array.push({ a: 1 }); 299 | // deno-lint-ignore no-explicit-any 300 | (observable.array[0] as any).a = 2; 301 | assertEquals(changeSpy.calls.length, 3); 302 | assertChange( 303 | changeSpy.calls[1].args[0], 304 | ".array", 305 | ChangeType.Add, 306 | undefined, 307 | [{ a: 2 }], 308 | ); 309 | assertChange(changeSpy.calls[2].args[0], ".array.0.a", ChangeType.Set, 1); 310 | }), 311 | ); 312 | 313 | test( 314 | "[Observable@change] reacts to removing elements from nested arrays", 315 | observableContext((observable, changeSpy) => { 316 | observable.array = [1, 2, 3]; 317 | observable.array.pop(); 318 | observable.array.shift(); 319 | assertEquals(changeSpy.calls.length, 3); 320 | assertChange( 321 | changeSpy.calls[1].args[0], 322 | ".array", 323 | ChangeType.Remove, 324 | undefined, 325 | 3, 326 | ); 327 | assertChange( 328 | changeSpy.calls[2].args[0], 329 | ".array", 330 | ChangeType.Remove, 331 | undefined, 332 | 1, 333 | ); 334 | }), 335 | ); 336 | 337 | test( 338 | "[Observable@change] reacts to using splice on nested arrays", 339 | observableContext((observable, changeSpy) => { 340 | observable.array = [1, 2, 3]; 341 | observable.array.splice(1, 1, 4, 5); 342 | assertEquals(changeSpy.calls.length, 3); 343 | assertChange( 344 | changeSpy.calls[1].args[0], 345 | ".array", 346 | ChangeType.Remove, 347 | undefined, 348 | [2], 349 | ); 350 | assertChange( 351 | changeSpy.calls[2].args[0], 352 | ".array", 353 | ChangeType.Add, 354 | undefined, 355 | [4, 5], 356 | ); 357 | }), 358 | ); 359 | 360 | test( 361 | "[Observable@change] reacts to using splice without adding elements", 362 | observableContext((observable, changeSpy) => { 363 | observable.array = [1, 2, 3]; 364 | observable.array.splice(1, 1); 365 | assertEquals(changeSpy.calls.length, 2); 366 | assertChange( 367 | changeSpy.calls[1].args[0], 368 | ".array", 369 | ChangeType.Remove, 370 | undefined, 371 | [2], 372 | ); 373 | }), 374 | ); 375 | 376 | test( 377 | "[Observable@change] reacts to sorting nested arrays", 378 | observableContext((observable, changeSpy) => { 379 | observable.array = [1, 2, 3]; 380 | observable.array.sort(); 381 | assertEquals(changeSpy.calls.length, 2); 382 | assertChange(changeSpy.calls[1].args[0], ".array", ChangeType.Sort); 383 | }), 384 | ); 385 | 386 | test("[Observable@change] handles nested observables", () => { 387 | const a = new Observable<{ b?: number }>({}); 388 | const b = new Observable<{ c?: typeof a }>({}); 389 | const changeSpy = spy(); 390 | const anotherSpy = spy(); 391 | b.addEventListener("change", changeSpy); 392 | a.addEventListener("change", anotherSpy); 393 | b.c = a; 394 | b.c.b = 1; 395 | assertEquals(changeSpy.calls.length, 1); 396 | assertChange(changeSpy.calls[0].args[0], ".c", ChangeType.Set); 397 | assertEquals(anotherSpy.calls.length, 1); 398 | assertChange(anotherSpy.calls[0].args[0], ".b", ChangeType.Set); 399 | }); 400 | 401 | test("[Observable@change] handles array of observables", () => { 402 | class A extends Observable<{ b?: number }> {} 403 | const a = new A({}); 404 | const b = new Observable<{ c: Array }>({ c: [] }); 405 | const changeSpy = spy(); 406 | const anotherSpy = spy(); 407 | b.c.push(a); 408 | b.addEventListener("change", changeSpy); 409 | a.addEventListener("change", anotherSpy); 410 | b.c[0].b = 10; 411 | b.c.push(a); 412 | assertEquals(changeSpy.calls.length, 1); 413 | assertChange(changeSpy.calls[0].args[0], ".c", ChangeType.Add, undefined, [ 414 | a, 415 | ]); 416 | assertEquals(anotherSpy.calls.length, 1); 417 | assertChange(anotherSpy.calls[0].args[0], ".b", ChangeType.Set); 418 | }); 419 | 420 | test( 421 | "[Observable#set] resets the observable with given properties", 422 | observableContext((observable, changeSpy) => { 423 | const attributes = { answer: 1 }; 424 | observable.set(attributes); 425 | assertEquals(toPlainObject(observable), attributes); 426 | assertEquals(changeSpy.calls.length, 4); 427 | }), 428 | ); 429 | 430 | test( 431 | "[Observable#assign] assigns given properties to the observable", 432 | observableContext((observable, changeSpy) => { 433 | const attributes = { 434 | answer: 4, 435 | question: "2x2", 436 | }; 437 | observable.assign(attributes); 438 | assertEquals(toPlainObject(observable), { 439 | answer: 4, 440 | question: "2x2", 441 | object: { name: "Zaphod", heads: 1 }, 442 | }); 443 | assertEquals(changeSpy.calls.length, 2); 444 | }), 445 | ); 446 | 447 | test( 448 | "[Observable#merge] merges given properties into the observable", 449 | observableContext((observable, changeSpy) => { 450 | const attributes = { 451 | answer: 1, 452 | array: [1], 453 | }; 454 | observable.assign(attributes); 455 | assertEquals(toPlainObject(observable), { 456 | answer: 1, 457 | question: "", 458 | object: { name: "Zaphod", heads: 1 }, 459 | array: [1], 460 | }); 461 | assertEquals(changeSpy.calls.length, 2); 462 | }), 463 | ); 464 | 465 | test( 466 | "[Observable#set] merges nested properties", 467 | observableContext((observable, changeSpy) => { 468 | observable.merge({ object: { name: "Ford" } }); 469 | assertEquals(toPlainObject(observable), { 470 | answer: 42, 471 | question: "", 472 | object: { name: "Ford", heads: 1 }, 473 | }); 474 | assertEquals(changeSpy.calls.length, 1); 475 | }), 476 | ); 477 | 478 | test( 479 | "[Observable#set] merges nested arrays", 480 | observableContext((observable, changeSpy) => { 481 | observable.array = [1, 2, 3]; 482 | const arr: Array = []; 483 | arr[0] = 4; 484 | arr[3] = 5; 485 | observable.merge({ array: arr }); 486 | assertEquals(observable.array, [4, 2, 3, 5]); 487 | assertEquals(changeSpy.calls.length, 3); 488 | }), 489 | ); 490 | 491 | test("[Observable#toJSON] returns a copy of observable for JSON stringification", () => { 492 | assertEquals(toPlainObject(new Observable({ a: 1, b: 2 })), { 493 | a: 1, 494 | b: 2, 495 | }); 496 | assertEquals( 497 | new Observable({ a: 1, b: 2 }).toJSON() instanceof Observable, 498 | false, 499 | ); 500 | }); 501 | -------------------------------------------------------------------------------- /tests/observer_test.ts: -------------------------------------------------------------------------------- 1 | import "./dom.ts"; 2 | import { assertEquals, Spy, spy } from "./test_deps.ts"; 3 | import { ChangeEvent, ChangeType, Observable } from "../observable.ts"; 4 | import { observe, observer } from "../observer.ts"; 5 | 6 | const { test } = Deno; 7 | const { LitElement } = await import("../deps.ts"); 8 | 9 | @observer() 10 | class ObserverClass extends LitElement { 11 | @observe() 12 | // deno-lint-ignore no-explicit-any 13 | $: Observable = new Observable({ a: 20 }); 14 | } 15 | 16 | customElements.define("c-component", ObserverClass); 17 | 18 | const componentContext = (callback: (component: ObserverClass) => void) => { 19 | return () => { 20 | callback(document.createElement("c-component") as ObserverClass); 21 | }; 22 | }; 23 | 24 | test( 25 | "[ObserverElement#constructor] creates accessors for observable properties", 26 | componentContext((component) => { 27 | const descriptor = Object.getOwnPropertyDescriptor( 28 | component, 29 | "$", 30 | )!; 31 | assertEquals(descriptor.get instanceof Function, true); 32 | assertEquals(descriptor.set instanceof Function, true); 33 | }), 34 | ); 35 | 36 | test( 37 | "[ObserverElement#observable] subscribes to observable change event", 38 | componentContext((component) => { 39 | const observable = new Observable({ a: 20 }); 40 | observable.addEventListener = spy(); 41 | component.$ = observable; 42 | assertEquals((observable.addEventListener as Spy).calls.length, 1); 43 | }), 44 | ); 45 | 46 | test( 47 | "[ObserverElement#observable] does not subscribe twice to the same observable", 48 | componentContext((component) => { 49 | const observable = new Observable({}); 50 | observable.addEventListener = spy(); 51 | component.$ = observable; 52 | component.$ = observable; 53 | assertEquals((observable.addEventListener as Spy).calls.length, 1); 54 | }), 55 | ); 56 | 57 | test( 58 | "[ObserverElement#observable] unsubscribes from the old observable when replacing", 59 | componentContext((component) => { 60 | const oldobservable = new Observable({}); 61 | component.$ = oldobservable; 62 | oldobservable.removeEventListener = spy(); 63 | component.$ = new Observable({}); 64 | assertEquals( 65 | (oldobservable.removeEventListener as Spy).calls.length, 66 | 1, 67 | ); 68 | }), 69 | ); 70 | 71 | test( 72 | "[ObserverElement#observable] schedules updates when observable changes", 73 | componentContext((component) => { 74 | const observable = new Observable({ a: { b: 1 } }); 75 | const updateSpy: Spy = spy(); 76 | component.requestUpdate = updateSpy; 77 | component.$ = observable.a; 78 | observable.a.b = 2; 79 | assertEquals(updateSpy.calls[0].args[0], "$"); 80 | assertEquals(updateSpy.calls[1].args[0], "$.b"); 81 | assertEquals( 82 | (updateSpy.calls[1].args[1] as ChangeEvent) 83 | .path, 84 | ".a.b", 85 | ); 86 | assertEquals( 87 | (updateSpy.calls[1].args[1] as ChangeEvent) 88 | .kind, 89 | ChangeType.Set, 90 | ); 91 | }), 92 | ); 93 | -------------------------------------------------------------------------------- /tests/rest-repository_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals, Stub, stub } from "./test_deps.ts"; 2 | import { RESTRepository } from "../rest-repository.ts"; 3 | import { Result } from "../result.ts"; 4 | 5 | const { test } = Deno; 6 | 7 | let fetchStub: Stub; 8 | 9 | const repositoryContext = ( 10 | callback: (repository: RESTRepository) => void, 11 | body?: unknown, 12 | status?: number, 13 | ) => { 14 | return async () => { 15 | fetchStub = stub( 16 | window, 17 | "fetch", 18 | () => 19 | Promise.resolve( 20 | body !== undefined 21 | ? new Response(JSON.stringify(body), { 22 | status: status || 200, 23 | headers: { "content-type": "application/json" }, 24 | }) 25 | : new Response(undefined, { status: status || 200 }), 26 | ), 27 | ); 28 | const repository = new RESTRepository(Object, "/things"); 29 | await callback(repository); 30 | fetchStub.restore(); 31 | }; 32 | }; 33 | 34 | test( 35 | "[RESTRepository#constructor] checks if an entity was persisted", 36 | repositoryContext((repository) => { 37 | assertEquals(repository instanceof RESTRepository, true); 38 | }), 39 | ); 40 | 41 | test( 42 | "[RESTRepository#has] checks if an entity was persisted", 43 | repositoryContext(async (repository) => { 44 | assertEquals(await repository.has("1"), Result.ok(true)); 45 | assertEquals(await repository.has(""), Result.ok(false)); 46 | }), 47 | ); 48 | 49 | test( 50 | "[RESTRepository#query] queries an url with optional search parameters", 51 | repositoryContext(async (repository) => { 52 | const result = await repository.query<{ a: number }[]>( 53 | { a: "1", b: "1" }, 54 | "/abc", 55 | ); 56 | assertEquals(fetchStub.calls[0].args[0], "/things/abc?a=1&b=1"); 57 | assertEquals(result.ok, true); 58 | assertEquals(result.value, [{ a: 1 }]); 59 | }, [{ a: 1 }]), 60 | ); 61 | 62 | test( 63 | "[RESTRepository#command]", 64 | repositoryContext(async (repository) => { 65 | const body = { a: "1", b: "1" }; 66 | const result = await repository.command<{ a: number }[]>(body, "/abc"); 67 | assertEquals(fetchStub.calls[0].args, ["/things/abc", { 68 | method: "POST", 69 | headers: { 70 | "Content-Type": "application/json", 71 | }, 72 | body: JSON.stringify(body), 73 | }]); 74 | assertEquals(result.ok, true); 75 | assertEquals(result.value, [{ a: 1 }]); 76 | }, [{ a: 1 }]), 77 | ); 78 | 79 | test( 80 | "[RESTRepository#list] queries REST endpoint with optional search parameters", 81 | repositoryContext(async (repository) => { 82 | await repository.list({ a: "1", b: "1" }); 83 | assertEquals(fetchStub.calls[0].args[0], "/things?a=1&b=1"); 84 | }), 85 | ); 86 | 87 | test( 88 | "[RESTRepository#list] queries an endpoint without search parameters", 89 | repositoryContext(async (repository) => { 90 | await repository.list(); 91 | assertEquals(fetchStub.calls[0].args[0], "/things"); 92 | }), 93 | ); 94 | 95 | test( 96 | "[RESTRepository#list] proxies failed response when fetch fails", 97 | repositoryContext( 98 | async (repository) => { 99 | const result = await repository.list(); 100 | assertEquals(fetchStub.calls[0].args[0], "/things"); 101 | assertEquals(result.ok, false); 102 | assertEquals(result.value instanceof Response, true); 103 | }, 104 | undefined, 105 | 404, 106 | ), 107 | ); 108 | 109 | test( 110 | "[RESTRepository#read] fetches entity from endpoint by id", 111 | repositoryContext(async (repository) => { 112 | const result = await repository.read("a"); 113 | assertEquals(fetchStub.calls[0].args[0], "/things/a"); 114 | assertEquals(result.ok, true); 115 | assertEquals(result.value, { _id: "a" }); 116 | }, { _id: "a" }), 117 | ); 118 | 119 | test( 120 | "[RESTRepository#read] proxies failed response when fetch fails", 121 | repositoryContext( 122 | async (repository) => { 123 | const result = await repository.read("a"); 124 | assertEquals(fetchStub.calls[0].args[0], "/things/a"); 125 | assertEquals(result.ok, false); 126 | assertEquals(result.value instanceof Response, true); 127 | }, 128 | undefined, 129 | 404, 130 | ), 131 | ); 132 | 133 | test( 134 | "[RESTRepository#save] persists a new entity if it does not exist", 135 | repositoryContext(async (repository) => { 136 | const request = { a: 1 }; 137 | await repository.save(request); 138 | assertEquals(fetchStub.calls[0].args, ["/things", { 139 | method: "POST", 140 | headers: { 141 | "Content-Type": "application/json", 142 | }, 143 | body: JSON.stringify(request), 144 | }]); 145 | }), 146 | ); 147 | 148 | test( 149 | "[RESTRepository#save] updates an existing entity", 150 | repositoryContext(async (repository) => { 151 | const request = { _id: "a", a: 1 }; 152 | await repository.save(request); 153 | assertEquals(fetchStub.calls[0].args, ["/things/a", { 154 | method: "PUT", 155 | headers: { 156 | "Content-Type": "application/json", 157 | }, 158 | body: JSON.stringify(request), 159 | }]); 160 | }), 161 | ); 162 | 163 | test( 164 | "[RESTRepository#delete] removes an existing entity", 165 | repositoryContext(async (repository) => { 166 | await repository.delete("a"); 167 | assertEquals(fetchStub.calls[0].args, ["/things/a", { 168 | headers: { 169 | "Content-Type": "application/json", 170 | }, 171 | method: "DELETE", 172 | }]); 173 | }), 174 | ); 175 | 176 | /* 177 | TODO: check the test case 178 | test( 179 | "[RESTRepository#delete] returns if the entity has not been persisted", 180 | repositoryContext(async (repository) => { 181 | const request = { a: 1 }; 182 | fetchStub.returns = [Promise.resolve(Result.ok(undefined))]; 183 | await repository.delete(request.a.toString()); 184 | assertEquals(fetchStub.calls.length, 0); 185 | }), 186 | ); */ 187 | 188 | test( 189 | "[RESTRepository.fetch] calls Fetch API supplying default headers", 190 | repositoryContext(async () => { 191 | const result = await RESTRepository.fetch(""); 192 | assertEquals(result, Result.ok(undefined)); 193 | assertEquals(fetchStub.calls[0].args, [ 194 | "", 195 | RESTRepository.init, 196 | ]); 197 | }), 198 | ); 199 | 200 | test( 201 | "[RESTRepository.fetch] handles redirected response", 202 | repositoryContext( 203 | async () => { 204 | const result = await RESTRepository.fetch(""); 205 | assertEquals(result, Result.ok(undefined)); 206 | }, 207 | undefined, 208 | 304, 209 | ), 210 | ); 211 | 212 | test( 213 | "[RESTRepository.fetch] handles json response", 214 | repositoryContext( 215 | async () => { 216 | const result = await RESTRepository.fetch(""); 217 | assertEquals(result, Result.ok({ a: 1 })); 218 | }, 219 | { a: 1 }, 220 | ), 221 | ); 222 | 223 | test( 224 | "[RESTRepository.fetch] returns Fetch API response if request fails", 225 | repositoryContext( 226 | async () => { 227 | const result = await RESTRepository.fetch(""); 228 | assertEquals(result.ok, false); 229 | assertEquals(result.value instanceof Response, true); 230 | assertEquals((result.value as Response).ok, false); 231 | }, 232 | undefined, 233 | 404, 234 | ), 235 | ); 236 | 237 | /* 238 | TODO: add test 239 | test("returns error if Fetch API promise fails" async () => { 240 | globalThis.fetch = jest.fn(() => Promise.reject("error")); 241 | const result = await RESTRepository.fetch(""); 242 | assertEquals(result.ok, false); 243 | assertEquals(result.value, "error"); 244 | }); 245 | */ 246 | -------------------------------------------------------------------------------- /tests/test_deps.ts: -------------------------------------------------------------------------------- 1 | export { 2 | assert, 3 | assertEquals, 4 | assertThrows, 5 | equal, 6 | } from "https://deno.land/std@0.190.0/testing/asserts.ts"; 7 | export { spy, stub } from "https://deno.land/std@0.190.0/testing/mock.ts"; 8 | export type { Spy, Stub } from "https://deno.land/std@0.190.0/testing/mock.ts"; 9 | export { parseHTML } from "npm:linkedom@0.14.26"; 10 | --------------------------------------------------------------------------------