@async/framework 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +153 -24
- package/examples/cache/index.html +3 -3
- package/examples/components/main.js +2 -2
- package/examples/counter/index.html +2 -2
- package/examples/partials/index.html +2 -2
- package/examples/product/index.html +9 -9
- package/examples/router/index.html +2 -2
- package/examples/router/main.js +2 -2
- package/examples/server-call/index.html +2 -2
- package/examples/ssr/index.html +1 -1
- package/examples/ssr/main.js +2 -2
- package/examples/streaming/index.html +2 -2
- package/examples/streaming/main.js +2 -2
- package/package.json +9 -2
- package/src/app.js +73 -53
- package/src/attributes.js +50 -0
- package/src/cache.js +31 -16
- package/src/component.js +11 -4
- package/src/handlers.js +24 -5
- package/src/index.js +2 -0
- package/src/loader.js +71 -45
- package/src/partials.js +11 -4
- package/src/registry-store.js +257 -0
- package/src/router.js +42 -3
- package/src/server.js +12 -4
- package/src/signals.js +32 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
- Added a shared registry store behind `Async`, app runtimes, and concrete
|
|
6
|
+
registries so apps can inspect signals, handlers, server ids, routes,
|
|
7
|
+
partials, components, and split cache state from one place.
|
|
8
|
+
- Added configurable HTML attribute prefixes, with `async:*`, `signal:*`, and
|
|
9
|
+
`on:*` as the defaults plus explicit support for `data-async-*`,
|
|
10
|
+
`data-signal-*`, and `data-on-*`.
|
|
11
|
+
- Declared the UNPKG package entry explicitly so the package root can be used as
|
|
12
|
+
a no-build browser ESM CDN import.
|
|
13
|
+
- Documented the UNPKG import-map setup for importing `@async/framework` by
|
|
14
|
+
package name in no-build browser apps.
|
|
15
|
+
|
|
3
16
|
## 0.2.2 - 2026-06-17
|
|
4
17
|
|
|
5
18
|
- Fixed release doctor validation to accept the pipeline-generated GitHub
|
package/README.md
CHANGED
|
@@ -10,9 +10,9 @@ pnpm add @async/framework
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
```html
|
|
13
|
-
<main
|
|
13
|
+
<main async:container>
|
|
14
14
|
<button type="button" on:click="decrement">-</button>
|
|
15
|
-
<strong
|
|
15
|
+
<strong signal:text="count"></strong>
|
|
16
16
|
<button type="button" on:click="increment">+</button>
|
|
17
17
|
</main>
|
|
18
18
|
<script type="module" src="./main.js"></script>
|
|
@@ -69,12 +69,78 @@ pnpm add @async/framework
|
|
|
69
69
|
The package is ESM-only and supports Node.js 24 and newer for tests, examples,
|
|
70
70
|
and package lifecycle tooling. Browser consumers import ESM directly.
|
|
71
71
|
|
|
72
|
+
## CDN
|
|
73
|
+
|
|
74
|
+
The package is browser-ready ESM and can be loaded from UNPKG without a build
|
|
75
|
+
step. Use `@latest` for quick prototypes, and pin an exact version in
|
|
76
|
+
production:
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<main async:container>
|
|
80
|
+
<button type="button" on:click="increment">+</button>
|
|
81
|
+
<strong signal:text="count"></strong>
|
|
82
|
+
</main>
|
|
83
|
+
|
|
84
|
+
<script type="module">
|
|
85
|
+
import {
|
|
86
|
+
Async,
|
|
87
|
+
createSignal
|
|
88
|
+
} from "https://unpkg.com/@async/framework@latest";
|
|
89
|
+
|
|
90
|
+
Async.use({
|
|
91
|
+
signal: {
|
|
92
|
+
count: createSignal(0)
|
|
93
|
+
},
|
|
94
|
+
handler: {
|
|
95
|
+
increment() {
|
|
96
|
+
this.signals.update("count", (count) => count + 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
Async.start({ root: document });
|
|
102
|
+
</script>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
You can also use an import map so app code imports `@async/framework` by name:
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<script type="importmap">
|
|
109
|
+
{
|
|
110
|
+
"imports": {
|
|
111
|
+
"@async/framework": "https://unpkg.com/@async/framework@latest"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<script type="module">
|
|
117
|
+
import {
|
|
118
|
+
Async,
|
|
119
|
+
createSignal
|
|
120
|
+
} from "@async/framework";
|
|
121
|
+
|
|
122
|
+
Async.use({
|
|
123
|
+
signal: {
|
|
124
|
+
count: createSignal(0)
|
|
125
|
+
},
|
|
126
|
+
handler: {
|
|
127
|
+
increment() {
|
|
128
|
+
this.signals.update("count", (count) => count + 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
Async.start({ root: document });
|
|
134
|
+
</script>
|
|
135
|
+
```
|
|
136
|
+
|
|
72
137
|
## Core API
|
|
73
138
|
|
|
74
139
|
```js
|
|
75
140
|
import {
|
|
76
141
|
AsyncLoader,
|
|
77
142
|
Async,
|
|
143
|
+
attributeName,
|
|
78
144
|
asyncSignal,
|
|
79
145
|
createApp,
|
|
80
146
|
createCacheRegistry,
|
|
@@ -84,11 +150,13 @@ import {
|
|
|
84
150
|
createSignal,
|
|
85
151
|
createHandlerRegistry,
|
|
86
152
|
createPartialRegistry,
|
|
153
|
+
createRegistryStore,
|
|
87
154
|
createRouteRegistry,
|
|
88
155
|
createRouter,
|
|
89
156
|
createServerProxy,
|
|
90
157
|
createServerRegistry,
|
|
91
158
|
createSignalRegistry,
|
|
159
|
+
defineAttributeConfig,
|
|
92
160
|
defineApp,
|
|
93
161
|
defineCache,
|
|
94
162
|
defineComponent,
|
|
@@ -170,6 +238,50 @@ Naming rules:
|
|
|
170
238
|
Singular registry keys are canonical: `signal`, `handler`, `server`,
|
|
171
239
|
`partial`, `route`, `component`, and nested `cache.browser` / `cache.server`.
|
|
172
240
|
|
|
241
|
+
### Registry Inspection
|
|
242
|
+
|
|
243
|
+
`Async.registry` is the global inspection surface for registered app pieces.
|
|
244
|
+
Every runtime and concrete registry also points at the same backing store:
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
Async.registry.keys("signal");
|
|
248
|
+
Async.registry.entries("route");
|
|
249
|
+
Async.registry.snapshot();
|
|
250
|
+
|
|
251
|
+
const runtime = Async.start({ root: document });
|
|
252
|
+
|
|
253
|
+
runtime.registry.keys("handler");
|
|
254
|
+
runtime.signals.registry === runtime.registry;
|
|
255
|
+
runtime.browser.cache.registry === runtime.registry;
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Supported inspection types:
|
|
259
|
+
|
|
260
|
+
```txt
|
|
261
|
+
signal
|
|
262
|
+
handler
|
|
263
|
+
server
|
|
264
|
+
partial
|
|
265
|
+
route
|
|
266
|
+
component
|
|
267
|
+
cache.browser
|
|
268
|
+
cache.server
|
|
269
|
+
cache.browser.entries
|
|
270
|
+
cache.server.entries
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Browser runtime inspection exposes server ids as descriptors, not executable
|
|
274
|
+
server functions, and does not expose server cache contents:
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
runtime.registry.keys("server");
|
|
278
|
+
runtime.registry.get("server", "products.get");
|
|
279
|
+
// { id: "products.get", kind: "server" }
|
|
280
|
+
|
|
281
|
+
runtime.registry.snapshot().entries.server;
|
|
282
|
+
// {}
|
|
283
|
+
```
|
|
284
|
+
|
|
173
285
|
### Signals
|
|
174
286
|
|
|
175
287
|
```js
|
|
@@ -249,33 +361,50 @@ AsyncLoader scans regular HTML attributes:
|
|
|
249
361
|
|
|
250
362
|
| Attribute | Behavior |
|
|
251
363
|
| --- | --- |
|
|
252
|
-
| `
|
|
364
|
+
| `async:container` | Marks a scannable app root |
|
|
253
365
|
| `on:click="selectProduct"` | Delegated command event |
|
|
254
366
|
| `on:submit="preventDefault; save"` | Sequential command chain |
|
|
255
367
|
| `on:click="server.cart.add(productId)"` | Server command with signal args |
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
| `
|
|
260
|
-
| `
|
|
261
|
-
| `
|
|
262
|
-
| `
|
|
263
|
-
| `
|
|
368
|
+
| `signal:text="product.title"` | Text binding |
|
|
369
|
+
| `signal:value="productId"` | Form value binding with writeback |
|
|
370
|
+
| `signal:attr:disabled="product.$loading"` | Attribute binding |
|
|
371
|
+
| `signal:class:selected="selected"` | Class toggle |
|
|
372
|
+
| `async:boundary="product"` | Async or streamed replacement boundary |
|
|
373
|
+
| `async:loading="product"` | Boundary loading template |
|
|
374
|
+
| `async:ready="product"` | Boundary ready template |
|
|
375
|
+
| `async:error="product"` | Boundary error template |
|
|
264
376
|
|
|
265
377
|
```html
|
|
266
|
-
<section
|
|
267
|
-
<template
|
|
378
|
+
<section async:boundary="product">
|
|
379
|
+
<template async:loading="product">
|
|
268
380
|
<p>Loading...</p>
|
|
269
381
|
</template>
|
|
270
|
-
<template
|
|
271
|
-
<h1
|
|
382
|
+
<template async:ready="product">
|
|
383
|
+
<h1 signal:text="product.title"></h1>
|
|
272
384
|
</template>
|
|
273
|
-
<template
|
|
274
|
-
<p
|
|
385
|
+
<template async:error="product">
|
|
386
|
+
<p signal:text="product.$error.message"></p>
|
|
275
387
|
</template>
|
|
276
388
|
</section>
|
|
277
389
|
```
|
|
278
390
|
|
|
391
|
+
The default prefixes are `async:`, `signal:`, and `on:`. You can switch to
|
|
392
|
+
data attributes when a host needs that shape:
|
|
393
|
+
|
|
394
|
+
```js
|
|
395
|
+
Async.start({
|
|
396
|
+
root: document,
|
|
397
|
+
attributes: {
|
|
398
|
+
async: "data-async-",
|
|
399
|
+
signal: "data-signal-",
|
|
400
|
+
on: "data-on-"
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
That maps to `data-async-container`, `data-on-click="save"`, and
|
|
406
|
+
`data-signal-text="product.title"`.
|
|
407
|
+
|
|
279
408
|
### Command Events
|
|
280
409
|
|
|
281
410
|
`on:*` works with any native DOM event name. `on:mount` and `on:visible` are
|
|
@@ -417,13 +546,13 @@ Router modes:
|
|
|
417
546
|
CSR startup can use an empty route boundary:
|
|
418
547
|
|
|
419
548
|
```html
|
|
420
|
-
<main
|
|
549
|
+
<main async:container>
|
|
421
550
|
<nav>
|
|
422
551
|
<a href="/">Home</a>
|
|
423
552
|
<a href="/products/sku-1">Product</a>
|
|
424
553
|
</nav>
|
|
425
554
|
|
|
426
|
-
<section
|
|
555
|
+
<section async:boundary="route"></section>
|
|
427
556
|
</main>
|
|
428
557
|
```
|
|
429
558
|
|
|
@@ -520,10 +649,10 @@ const response = await serverRuntime.render("/products/123");
|
|
|
520
649
|
The returned HTML includes a route boundary plus a JSON snapshot:
|
|
521
650
|
|
|
522
651
|
```html
|
|
523
|
-
<section
|
|
652
|
+
<section async:boundary="route">
|
|
524
653
|
<!-- server-rendered route partial -->
|
|
525
654
|
</section>
|
|
526
|
-
<script type="application/json"
|
|
655
|
+
<script type="application/json" async:snapshot>{}</script>
|
|
527
656
|
```
|
|
528
657
|
|
|
529
658
|
Browser activation scans the existing HTML and attaches events. It does not
|
|
@@ -558,8 +687,8 @@ const Toggle = defineComponent(function Toggle() {
|
|
|
558
687
|
<button
|
|
559
688
|
type="button"
|
|
560
689
|
on:click="${toggle}"
|
|
561
|
-
|
|
562
|
-
|
|
690
|
+
signal:class:selected="${selected.id}"
|
|
691
|
+
signal:attr:aria-pressed="${selected.id}"
|
|
563
692
|
>
|
|
564
693
|
Toggle
|
|
565
694
|
</button>
|
|
@@ -597,7 +726,7 @@ loader.swap(
|
|
|
597
726
|
"product",
|
|
598
727
|
`
|
|
599
728
|
<article>
|
|
600
|
-
<h1
|
|
729
|
+
<h1 signal:text="product.title"></h1>
|
|
601
730
|
<button type="button" on:click="selectProduct">Select</button>
|
|
602
731
|
</article>
|
|
603
732
|
`
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<title>AsyncLoader Cache</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<main
|
|
9
|
+
<main async:container>
|
|
10
10
|
<button type="button" on:click="cacheDemo.loadProduct">Load product</button>
|
|
11
|
-
<p>Product: <strong
|
|
12
|
-
<p>Server calls: <strong
|
|
11
|
+
<p>Product: <strong signal:text="cacheDemo.title"></strong></p>
|
|
12
|
+
<p>Server calls: <strong signal:text="cacheDemo.calls"></strong></p>
|
|
13
13
|
</main>
|
|
14
14
|
<script type="module" src="./main.js"></script>
|
|
15
15
|
</body>
|
|
@@ -14,8 +14,8 @@ const Toggle = defineComponent(function Toggle() {
|
|
|
14
14
|
<button
|
|
15
15
|
type="button"
|
|
16
16
|
on:click="${toggle}"
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
signal:class:selected="${selected.id}"
|
|
18
|
+
signal:attr:aria-pressed="${selected.id}"
|
|
19
19
|
>
|
|
20
20
|
Toggle
|
|
21
21
|
</button>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<title>AsyncLoader Counter</title>
|
|
6
6
|
</head>
|
|
7
|
-
<body
|
|
7
|
+
<body async:container>
|
|
8
8
|
<main>
|
|
9
|
-
<p>Count: <strong
|
|
9
|
+
<p>Count: <strong signal:text="counter.count"></strong></p>
|
|
10
10
|
<button type="button" on:click="counter.decrement">-</button>
|
|
11
11
|
<button type="button" on:click="counter.increment">+</button>
|
|
12
12
|
</main>
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<title>AsyncLoader Partials</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<main
|
|
9
|
+
<main async:container>
|
|
10
10
|
<button type="button" on:click="partialsDemo.loadProduct">Load product</button>
|
|
11
|
-
<section
|
|
11
|
+
<section async:boundary="product"></section>
|
|
12
12
|
</main>
|
|
13
13
|
<script type="module" src="./main.js"></script>
|
|
14
14
|
</body>
|
|
@@ -4,26 +4,26 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<title>AsyncLoader Product</title>
|
|
6
6
|
</head>
|
|
7
|
-
<body
|
|
7
|
+
<body async:container>
|
|
8
8
|
<main>
|
|
9
9
|
<label>
|
|
10
10
|
Product
|
|
11
|
-
<select
|
|
11
|
+
<select signal:value="productId">
|
|
12
12
|
<option value="sku-1">Mechanical Keyboard</option>
|
|
13
13
|
<option value="sku-2">Studio Headphones</option>
|
|
14
14
|
</select>
|
|
15
15
|
</label>
|
|
16
16
|
|
|
17
|
-
<section
|
|
18
|
-
<template
|
|
17
|
+
<section async:boundary="product">
|
|
18
|
+
<template async:loading="product">
|
|
19
19
|
<p>Loading product...</p>
|
|
20
20
|
</template>
|
|
21
|
-
<template
|
|
22
|
-
<h1
|
|
23
|
-
<p
|
|
21
|
+
<template async:ready="product">
|
|
22
|
+
<h1 signal:text="product.title"></h1>
|
|
23
|
+
<p signal:text="product.description"></p>
|
|
24
24
|
</template>
|
|
25
|
-
<template
|
|
26
|
-
<p
|
|
25
|
+
<template async:error="product">
|
|
26
|
+
<p signal:text="product.$error.message"></p>
|
|
27
27
|
</template>
|
|
28
28
|
</section>
|
|
29
29
|
</main>
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<title>AsyncLoader CSR Router</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<main
|
|
9
|
+
<main async:container>
|
|
10
10
|
<nav>
|
|
11
11
|
<a href="/">Home</a>
|
|
12
12
|
<a href="/products/sku-1">Product</a>
|
|
13
13
|
</nav>
|
|
14
|
-
<section
|
|
14
|
+
<section async:boundary="route"></section>
|
|
15
15
|
</main>
|
|
16
16
|
<script type="module" src="./main.js"></script>
|
|
17
17
|
</body>
|
package/examples/router/main.js
CHANGED
|
@@ -26,14 +26,14 @@ Async.use({
|
|
|
26
26
|
"routerDemo.home"() {
|
|
27
27
|
return html`
|
|
28
28
|
<h1>Home</h1>
|
|
29
|
-
<p>Cart: <strong
|
|
29
|
+
<p>Cart: <strong signal:text="routerDemo.cartCount"></strong></p>
|
|
30
30
|
`;
|
|
31
31
|
},
|
|
32
32
|
"routerDemo.product.page"({ id }) {
|
|
33
33
|
return html`
|
|
34
34
|
<article>
|
|
35
35
|
<h1>Product ${id}</h1>
|
|
36
|
-
<p>Cart: <strong
|
|
36
|
+
<p>Cart: <strong signal:text="routerDemo.cartCount"></strong></p>
|
|
37
37
|
<button type="button" on:click="server.routerDemo.cart.add(routerDemo.productId)">Add</button>
|
|
38
38
|
</article>
|
|
39
39
|
`;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>AsyncLoader Server Call</title>
|
|
7
7
|
</head>
|
|
8
8
|
<body>
|
|
9
|
-
<main
|
|
9
|
+
<main async:container>
|
|
10
10
|
<form on:submit="preventDefault; server.serverCall.products.save(serverCall.productId, $form)">
|
|
11
11
|
<label>
|
|
12
12
|
Title
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
</label>
|
|
15
15
|
<button type="submit">Save</button>
|
|
16
16
|
</form>
|
|
17
|
-
<p>Saved: <strong
|
|
17
|
+
<p>Saved: <strong signal:text="serverCall.savedTitle"></strong></p>
|
|
18
18
|
</main>
|
|
19
19
|
<script type="module" src="./main.js"></script>
|
|
20
20
|
</body>
|
package/examples/ssr/index.html
CHANGED
package/examples/ssr/main.js
CHANGED
|
@@ -56,7 +56,7 @@ serverApp.use({
|
|
|
56
56
|
<article>
|
|
57
57
|
<h1>${product.title}</h1>
|
|
58
58
|
<p>${product.id}</p>
|
|
59
|
-
<button type="button" on:click="ssrDemo.selectProduct"
|
|
59
|
+
<button type="button" on:click="ssrDemo.selectProduct" signal:class:selected="ssrDemo.selected">
|
|
60
60
|
Select
|
|
61
61
|
</button>
|
|
62
62
|
</article>
|
|
@@ -80,7 +80,7 @@ serverRuntime.destroy();
|
|
|
80
80
|
|
|
81
81
|
document.querySelector("#app").innerHTML = response.html;
|
|
82
82
|
|
|
83
|
-
const snapshot = JSON.parse(document.querySelector("[
|
|
83
|
+
const snapshot = JSON.parse(document.querySelector("[async\\:snapshot]").textContent);
|
|
84
84
|
const browserApp = defineApp(sharedDefinition());
|
|
85
85
|
createApp(browserApp, {
|
|
86
86
|
root: document,
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<title>AsyncLoader Streaming</title>
|
|
6
6
|
</head>
|
|
7
|
-
<body
|
|
7
|
+
<body async:container>
|
|
8
8
|
<main>
|
|
9
9
|
<button type="button" on:click="streamingDemo.streamProduct">Stream product</button>
|
|
10
|
-
<section
|
|
10
|
+
<section async:boundary="product">
|
|
11
11
|
<p>Waiting for streamed HTML...</p>
|
|
12
12
|
</section>
|
|
13
13
|
</main>
|
|
@@ -13,8 +13,8 @@ Async.use({
|
|
|
13
13
|
"product",
|
|
14
14
|
`
|
|
15
15
|
<article>
|
|
16
|
-
<h1
|
|
17
|
-
<button type="button" on:click="streamingDemo.select"
|
|
16
|
+
<h1 signal:text="streamingDemo.title"></h1>
|
|
17
|
+
<button type="button" on:click="streamingDemo.select" signal:class:selected="streamingDemo.selected">
|
|
18
18
|
Select
|
|
19
19
|
</button>
|
|
20
20
|
</article>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@async/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "No-build AsyncLoader app runtime with signals, command events, server calls, route partials, cache split, SSR activation, and streaming boundaries.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.1.0",
|
|
@@ -28,8 +28,15 @@
|
|
|
28
28
|
"web-framework"
|
|
29
29
|
],
|
|
30
30
|
"license": "MIT",
|
|
31
|
+
"unpkg": "./src/index.js",
|
|
31
32
|
"exports": {
|
|
32
|
-
".":
|
|
33
|
+
".": {
|
|
34
|
+
"unpkg": "./src/index.js",
|
|
35
|
+
"browser": "./src/index.js",
|
|
36
|
+
"import": "./src/index.js",
|
|
37
|
+
"default": "./src/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
33
40
|
},
|
|
34
41
|
"files": [
|
|
35
42
|
"CHANGELOG.md",
|