@rooted-adapters/azure-static-webapp 1.0.0-alpha.25 → 1.0.0-alpha.27

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.
@@ -1,10 +1,22 @@
1
1
  import { AdapterRoutes } from "@rooted/adapter";
2
2
  import { Plugin } from "vite";
3
3
  //#region src/adapter.d.mts
4
+ /**
5
+ * What Azure answers for a `:param` route. There's no default, because both are wrong somewhere,
6
+ * and which one hurts less depends on your site. See {@link azureStaticWebappAdapter}.
7
+ */
8
+ export type AzureDynamicRoutes = 'not-found' | 'catch-all';
4
9
  /**
5
10
  * Options for {@link azureStaticWebappAdapter}.
6
11
  */
7
12
  export type AzureStaticWebappAdapterOptions = {
13
+ /**
14
+ * What Azure answers for a `:param` route. Required, see {@link azureStaticWebappAdapter} for the trade-off.
15
+ *
16
+ * - `'not-found'`: real dynamic pages answer `404`. Every status for a path that isn't a route is right.
17
+ * - `'catch-all'`: real dynamic pages answer `200`, and so does anything deeper under the same prefix.
18
+ */
19
+ dynamicRoutes: AzureDynamicRoutes;
8
20
  /**
9
21
  * Manual route list for projects that don't use `generateRouteManifest`.
10
22
  * See {@link AdapterRoutes}.
@@ -14,16 +26,27 @@ export type AzureStaticWebappAdapterOptions = {
14
26
  /**
15
27
  * Adapter for Azure Static Web Apps.
16
28
  *
17
- * Writes `staticwebapp.config.json` to the output directory with routing rules built from the route manifest and/or manual `routes` option:
18
- * - Pre-rendered routes get explicit `200` entries so Azure serves their HTML directly.
19
- * - Everything else returns a `404` response that serves `404.html`, matching the
20
- * standard SPA behaviour: the shell loads and the client-side router takes over.
29
+ * Writes `staticwebapp.config.json` to the output directory. Pre-rendered pages are plain files, which Azure serves with a `200` on its own,
30
+ * and a path that doesn't resolve gets `404.html` with a real `404`.
31
+ *
32
+ * Azure can't match a `:param` route exactly. A wildcard is only allowed at the end of a rule and there's no per-segment match,
33
+ * so there's no rule that means "`/recipe/` plus exactly one segment". You have to pick which way it's wrong:
34
+ *
35
+ * **`'not-found'`** writes no rule for dynamic routes.
36
+ * - `/recipe/42/` serves the shell with a `404`. The page renders, because the browser-side router takes over, but crawlers drop it.
37
+ * If your dynamic pages are content you want found, that's most of your site gone from search.
38
+ * - Everything that isn't a route answers `404`, so there are no soft 404s.
39
+ *
40
+ * **`'catch-all'`** writes `/recipe/*` for `/recipe/:id/`, cut at the first `:param`.
41
+ * - `/recipe/42/` answers `200`.
42
+ * - So does `/recipe/42/extra/`, which isn't a route. The browser-side router renders your not-found page with a `200`.
43
+ * Nothing links there, so crawlers rarely see it, but it's a soft 404 when they do.
44
+ * - A route that starts with a `:param`, like `/:slug/`, becomes `/*`: every unknown path on the site answers `200`. The build warns when that happens.
45
+ * - Azure checks rules before files, so every real file under a wildcard gets a rule of its own to stay reachable.
46
+ * With `/*` that's every file in the build, and Azure caps the config at 20 KB. The build fails if it goes over.
21
47
  *
22
- * "Everything else" includes your `:param` routes, so `/recipe/42/` renders but answers `404`.
23
- * That's why this adapter leaves `dynamicRoutes` at its default.
24
- * Azure only supports a wildcard at the end of a route and has no per-segment match, so a `/recipe/*` rule would also claim `/recipe/42/extra/`,
25
- * trading a wrong `404` on a real page for a wrong `200` on a path that isn't one.
26
- * See [issue #311](https://github.com/Marvin-Brouwer/rooted/issues/311).
48
+ * `vite dev` and `vite preview` answer the same way as the option you pick.
49
+ * Neither option gives an exact answer. For that you need something that runs the route matching itself, like `@rooted-adapters/fastify`.
27
50
  *
28
51
  * @example `vite.config.ts`
29
52
  * ```ts
@@ -34,11 +57,12 @@ export type AzureStaticWebappAdapterOptions = {
34
57
  * export default rootedManifest({
35
58
  * plugins: [
36
59
  * generateRouteManifest({ glob: './src/**\/_routes.mts', root: './src/_routes.g.mts' }),
37
- * azureStaticWebappAdapter(),
60
+ * // Recipe pages should be indexed; a 200 on /recipe/42/extra/ is the price.
61
+ * azureStaticWebappAdapter({ dynamicRoutes: 'catch-all' }),
38
62
  * ],
39
63
  * })
40
64
  * ```
41
65
  */
42
- export declare function azureStaticWebappAdapter(options?: AzureStaticWebappAdapterOptions): Plugin[];
66
+ export declare function azureStaticWebappAdapter(options: AzureStaticWebappAdapterOptions): Plugin[];
43
67
  //#endregion
44
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp1cmUtc3RhdGljLXdlYmFwcC5kLm10cyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi9zcmMvYWRhcHRlci5tdHMiXSwibWFwcGluZ3MiOiI7Ozs7OztZQVdZOzs7OztFQUtYLFNBQVM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozt3QkErQk0seUJBQXlCLFVBQVUsa0NBQWtDIn0=
68
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp1cmUtc3RhdGljLXdlYmFwcC5kLm10cyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi9zcmMvYWRhcHRlci5tdHMiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7WUFnQlk7Ozs7WUFLQTs7Ozs7OztFQU9YLGVBQWU7Ozs7O0VBS2YsU0FBUzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O3dCQTJDTSx5QkFBeUIsU0FBUyxrQ0FBa0MifQ==
@@ -1,20 +1,72 @@
1
- import { writeFile } from "node:fs/promises";
1
+ import { Buffer } from "node:buffer";
2
+ import { readdir, writeFile } from "node:fs/promises";
2
3
  import path from "node:path";
3
- import { staticAdapter } from "@rooted/adapter";
4
+ import { catchAllPrefixes, staticAdapter, withTrailingSlash } from "@rooted/adapter";
5
+ //#region src/adapter/routes.mts
6
+ const configFileName = "staticwebapp.config.json";
7
+ /**
8
+ * Builds the `routes` array for `staticwebapp.config.json`.
9
+ *
10
+ * Azure only supports a wildcard at the end of a route, so a dynamic pattern can't be matched per segment.
11
+ * Each pattern is cut at its first `:param` and becomes a `<prefix>*` rule that rewrites to the fallback file, which answers `200`.
12
+ * That over-matches: `/recipe/:id/` becomes `/recipe/*`, so `/recipe/42/extra/` answers `200` too, and the browser-side router renders not-found.
13
+ * A pattern that starts with a `:param` becomes `/*`, which turns every unknown path into a soft 404.
14
+ *
15
+ * Azure applies route rules before it looks for a file, so a wildcard would also swallow the real files under its prefix.
16
+ * Every output file and pre-rendered page under a prefix gets a rule with no action ahead of the wildcards. Rules are first-match,
17
+ * so that stops evaluation and Azure serves the file as usual. Pages are listed as their `index.html`, because Azure matches that rule for the folder too.
18
+ * They're passed in as static paths rather than files, since the adapter runs before the pages are written.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * buildAzureRoutes(
23
+ * { staticPaths: ['/recipe/new/', '/about/'], dynamicPatterns: ['/recipe/:id/', '/recipe/:id/ingredients/'] },
24
+ * ['recipe/photo.jpg', 'assets/index.js'],
25
+ * '404.html',
26
+ * )
27
+ * // [
28
+ * // { route: '/recipe/photo.jpg' },
29
+ * // { route: '/recipe/new/index.html' },
30
+ * // { route: '/recipe/*', rewrite: '/404.html' },
31
+ * // ]
32
+ * ```
33
+ */
34
+ function buildAzureRoutes(routes, outputFiles, fallbackFileName) {
35
+ const prefixes = catchAllPrefixes(routes.dynamicPatterns);
36
+ const files = [...outputFiles.map((file) => `/${file}`).filter((file) => file !== `/${configFileName}`), ...routes.staticPaths.map((staticPath) => `${withTrailingSlash(staticPath)}index.html`)];
37
+ const passThrough = [...new Set(files)].filter((file) => prefixes.some((prefix) => file.startsWith(prefix))).map((file) => ({ route: file }));
38
+ const wildcards = prefixes.map((prefix) => ({
39
+ route: `${prefix}*`,
40
+ rewrite: `/${fallbackFileName}`
41
+ }));
42
+ return [...passThrough, ...wildcards];
43
+ }
44
+ //#endregion
4
45
  //#region src/adapter.mts
5
46
  /**
6
47
  * Adapter for Azure Static Web Apps.
7
48
  *
8
- * Writes `staticwebapp.config.json` to the output directory with routing rules built from the route manifest and/or manual `routes` option:
9
- * - Pre-rendered routes get explicit `200` entries so Azure serves their HTML directly.
10
- * - Everything else returns a `404` response that serves `404.html`, matching the
11
- * standard SPA behaviour: the shell loads and the client-side router takes over.
49
+ * Writes `staticwebapp.config.json` to the output directory. Pre-rendered pages are plain files, which Azure serves with a `200` on its own,
50
+ * and a path that doesn't resolve gets `404.html` with a real `404`.
51
+ *
52
+ * Azure can't match a `:param` route exactly. A wildcard is only allowed at the end of a rule and there's no per-segment match,
53
+ * so there's no rule that means "`/recipe/` plus exactly one segment". You have to pick which way it's wrong:
54
+ *
55
+ * **`'not-found'`** writes no rule for dynamic routes.
56
+ * - `/recipe/42/` serves the shell with a `404`. The page renders, because the browser-side router takes over, but crawlers drop it.
57
+ * If your dynamic pages are content you want found, that's most of your site gone from search.
58
+ * - Everything that isn't a route answers `404`, so there are no soft 404s.
12
59
  *
13
- * "Everything else" includes your `:param` routes, so `/recipe/42/` renders but answers `404`.
14
- * That's why this adapter leaves `dynamicRoutes` at its default.
15
- * Azure only supports a wildcard at the end of a route and has no per-segment match, so a `/recipe/*` rule would also claim `/recipe/42/extra/`,
16
- * trading a wrong `404` on a real page for a wrong `200` on a path that isn't one.
17
- * See [issue #311](https://github.com/Marvin-Brouwer/rooted/issues/311).
60
+ * **`'catch-all'`** writes `/recipe/*` for `/recipe/:id/`, cut at the first `:param`.
61
+ * - `/recipe/42/` answers `200`.
62
+ * - So does `/recipe/42/extra/`, which isn't a route. The browser-side router renders your not-found page with a `200`.
63
+ * Nothing links there, so crawlers rarely see it, but it's a soft 404 when they do.
64
+ * - A route that starts with a `:param`, like `/:slug/`, becomes `/*`: every unknown path on the site answers `200`. The build warns when that happens.
65
+ * - Azure checks rules before files, so every real file under a wildcard gets a rule of its own to stay reachable.
66
+ * With `/*` that's every file in the build, and Azure caps the config at 20 KB. The build fails if it goes over.
67
+ *
68
+ * `vite dev` and `vite preview` answer the same way as the option you pick.
69
+ * Neither option gives an exact answer. For that you need something that runs the route matching itself, like `@rooted-adapters/fastify`.
18
70
  *
19
71
  * @example `vite.config.ts`
20
72
  * ```ts
@@ -25,33 +77,49 @@ import { staticAdapter } from "@rooted/adapter";
25
77
  * export default rootedManifest({
26
78
  * plugins: [
27
79
  * generateRouteManifest({ glob: './src/**\/_routes.mts', root: './src/_routes.g.mts' }),
28
- * azureStaticWebappAdapter(),
80
+ * // Recipe pages should be indexed; a 200 on /recipe/42/extra/ is the price.
81
+ * azureStaticWebappAdapter({ dynamicRoutes: 'catch-all' }),
29
82
  * ],
30
83
  * })
31
84
  * ```
32
85
  */
33
86
  function azureStaticWebappAdapter(options) {
87
+ const dynamicRoutes = requireDynamicRoutes(options);
34
88
  return staticAdapter({
35
89
  name: "rooted:azure-static-webapp",
36
- routes: options?.routes,
37
- async setup({ outputDirectory, resolvedRoutes }) {
38
- const config = {
39
- routes: resolvedRoutes.staticPaths.filter((p) => p.split("/").filter(Boolean).length > 0).map((p) => ({
40
- route: p,
41
- serve: `/${p.split("/").filter(Boolean).join("/")}/index.html`,
42
- statusCode: 200
43
- })),
90
+ routes: options.routes,
91
+ dynamicRoutes,
92
+ async setup({ config, outputDirectory, resolvedRoutes }) {
93
+ const routes = dynamicRoutes === "catch-all" ? buildAzureRoutes(resolvedRoutes, await listFiles(outputDirectory), "404.html") : [];
94
+ if (dynamicRoutes === "catch-all" && catchAllPrefixes(resolvedRoutes.dynamicPatterns).includes("/")) config.logger.warn("[rooted:azure-static-webapp] a route starts with a :param, so the catch-all rule is /*. Every unknown path on the site now answers 200.");
95
+ const contents = JSON.stringify({
96
+ routes,
44
97
  trailingSlash: "auto",
45
98
  responseOverrides: { 404: {
46
99
  rewrite: "/404.html",
47
100
  statusCode: 404
48
101
  } }
49
- };
50
- await writeFile(path.join(outputDirectory, "staticwebapp.config.json"), JSON.stringify(config, void 0, 2), "utf8");
102
+ }, void 0, 2);
103
+ const size = Buffer.byteLength(contents, "utf8");
104
+ if (size > maxConfigSize) throw new Error(`[rooted:azure-static-webapp] staticwebapp.config.json is ${size} bytes, over Azure's limit of ${maxConfigSize}. Every file under a catch-all prefix needs its own rule, and a route that starts with a :param (like /:slug/) puts every file in the build under it.`);
105
+ await writeFile(path.join(outputDirectory, "staticwebapp.config.json"), contents, "utf8");
51
106
  }
52
107
  });
53
108
  }
109
+ const maxConfigSize = 20480;
110
+ function requireDynamicRoutes(options) {
111
+ const value = options?.dynamicRoutes;
112
+ if (value === "not-found" || value === "catch-all") return value;
113
+ throw new Error(`[rooted:azure-static-webapp] dynamicRoutes is required, and has to be 'not-found' or 'catch-all' (got ${JSON.stringify(value)}). Azure can't match a :param route exactly, so pick which way it's wrong: 'not-found' answers 404 on real dynamic pages, 'catch-all' answers 200 on paths below them that aren't routes.`);
114
+ }
115
+ /** Every file in the output directory, relative to it and with `/` separators. */
116
+ async function listFiles(outputDirectory) {
117
+ return (await readdir(outputDirectory, {
118
+ recursive: true,
119
+ withFileTypes: true
120
+ })).filter((entry) => entry.isFile()).map((entry) => path.relative(outputDirectory, path.join(entry.parentPath, entry.name)).split(path.sep).join("/"));
121
+ }
54
122
  //#endregion
55
123
  export { azureStaticWebappAdapter };
56
124
 
57
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp1cmUtc3RhdGljLXdlYmFwcC5tanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL2FkYXB0ZXIubXRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHdyaXRlRmlsZSB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnXG5cbmltcG9ydCB7IHN0YXRpY0FkYXB0ZXIgfSBmcm9tICdAcm9vdGVkL2FkYXB0ZXInXG5cbmltcG9ydCB0eXBlIHsgQWRhcHRlclJvdXRlcyB9IGZyb20gJ0Byb290ZWQvYWRhcHRlcidcbmltcG9ydCB0eXBlIHsgUGx1Z2luIH0gZnJvbSAndml0ZSdcblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyfS5cbiAqL1xuZXhwb3J0IHR5cGUgQXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyT3B0aW9ucyA9IHtcblx0LyoqXG5cdCAqIE1hbnVhbCByb3V0ZSBsaXN0IGZvciBwcm9qZWN0cyB0aGF0IGRvbid0IHVzZSBgZ2VuZXJhdGVSb3V0ZU1hbmlmZXN0YC5cblx0ICogU2VlIHtAbGluayBBZGFwdGVyUm91dGVzfS5cblx0ICovXG5cdHJvdXRlcz86IEFkYXB0ZXJSb3V0ZXNcbn1cblxuLyoqXG4gKiBBZGFwdGVyIGZvciBBenVyZSBTdGF0aWMgV2ViIEFwcHMuXG4gKlxuICogV3JpdGVzIGBzdGF0aWN3ZWJhcHAuY29uZmlnLmpzb25gIHRvIHRoZSBvdXRwdXQgZGlyZWN0b3J5IHdpdGggcm91dGluZyBydWxlcyBidWlsdCBmcm9tIHRoZSByb3V0ZSBtYW5pZmVzdCBhbmQvb3IgbWFudWFsIGByb3V0ZXNgIG9wdGlvbjpcbiAqIC0gUHJlLXJlbmRlcmVkIHJvdXRlcyBnZXQgZXhwbGljaXQgYDIwMGAgZW50cmllcyBzbyBBenVyZSBzZXJ2ZXMgdGhlaXIgSFRNTCBkaXJlY3RseS5cbiAqIC0gRXZlcnl0aGluZyBlbHNlIHJldHVybnMgYSBgNDA0YCByZXNwb25zZSB0aGF0IHNlcnZlcyBgNDA0Lmh0bWxgLCBtYXRjaGluZyB0aGVcbiAqICAgc3RhbmRhcmQgU1BBIGJlaGF2aW91cjogdGhlIHNoZWxsIGxvYWRzIGFuZCB0aGUgY2xpZW50LXNpZGUgcm91dGVyIHRha2VzIG92ZXIuXG4gKlxuICogXCJFdmVyeXRoaW5nIGVsc2VcIiBpbmNsdWRlcyB5b3VyIGA6cGFyYW1gIHJvdXRlcywgc28gYC9yZWNpcGUvNDIvYCByZW5kZXJzIGJ1dCBhbnN3ZXJzIGA0MDRgLlxuICogVGhhdCdzIHdoeSB0aGlzIGFkYXB0ZXIgbGVhdmVzIGBkeW5hbWljUm91dGVzYCBhdCBpdHMgZGVmYXVsdC5cbiAqIEF6dXJlIG9ubHkgc3VwcG9ydHMgYSB3aWxkY2FyZCBhdCB0aGUgZW5kIG9mIGEgcm91dGUgYW5kIGhhcyBubyBwZXItc2VnbWVudCBtYXRjaCwgc28gYSBgL3JlY2lwZS8qYCBydWxlIHdvdWxkIGFsc28gY2xhaW0gYC9yZWNpcGUvNDIvZXh0cmEvYCxcbiAqIHRyYWRpbmcgYSB3cm9uZyBgNDA0YCBvbiBhIHJlYWwgcGFnZSBmb3IgYSB3cm9uZyBgMjAwYCBvbiBhIHBhdGggdGhhdCBpc24ndCBvbmUuXG4gKiBTZWUgW2lzc3VlICMzMTFdKGh0dHBzOi8vZ2l0aHViLmNvbS9NYXJ2aW4tQnJvdXdlci9yb290ZWQvaXNzdWVzLzMxMSkuXG4gKlxuICogQGV4YW1wbGUgYHZpdGUuY29uZmlnLnRzYFxuICogYGBgdHNcbiAqIGltcG9ydCB7IHJvb3RlZE1hbmlmZXN0IH0gZnJvbSAnQHJvb3RlZC9hcHBsaWNhdGlvbidcbiAqIGltcG9ydCB7IGdlbmVyYXRlUm91dGVNYW5pZmVzdCB9IGZyb20gJ0Byb290ZWQvcm91dGVyL21hbmlmZXN0J1xuICogaW1wb3J0IHsgYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyIH0gZnJvbSAnQHJvb3RlZC1hZGFwdGVycy9henVyZS1zdGF0aWMtd2ViYXBwJ1xuICpcbiAqIGV4cG9ydCBkZWZhdWx0IHJvb3RlZE1hbmlmZXN0KHtcbiAqICAgcGx1Z2luczogW1xuICogICAgIGdlbmVyYXRlUm91dGVNYW5pZmVzdCh7IGdsb2I6ICcuL3NyYy8qKlxcL19yb3V0ZXMubXRzJywgcm9vdDogJy4vc3JjL19yb3V0ZXMuZy5tdHMnIH0pLFxuICogICAgIGF6dXJlU3RhdGljV2ViYXBwQWRhcHRlcigpLFxuICogICBdLFxuICogfSlcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyKG9wdGlvbnM/OiBBenVyZVN0YXRpY1dlYmFwcEFkYXB0ZXJPcHRpb25zKTogUGx1Z2luW10ge1xuXHRyZXR1cm4gc3RhdGljQWRhcHRlcih7XG5cdFx0bmFtZTogJ3Jvb3RlZDphenVyZS1zdGF0aWMtd2ViYXBwJyxcblx0XHRyb3V0ZXM6IG9wdGlvbnM/LnJvdXRlcyxcblx0XHRhc3luYyBzZXR1cCh7IG91dHB1dERpcmVjdG9yeSwgcmVzb2x2ZWRSb3V0ZXMgfSkge1xuXHRcdFx0Y29uc3Qgc3RhdGljUm91dGVzOiBBenVyZVJvdXRlW10gPSByZXNvbHZlZFJvdXRlcy5zdGF0aWNQYXRoc1xuXHRcdFx0XHQuZmlsdGVyKHAgPT4gcC5zcGxpdCgnLycpLmZpbHRlcihCb29sZWFuKS5sZW5ndGggPiAwKVxuXHRcdFx0XHQubWFwKHAgPT4gKHtcblx0XHRcdFx0XHRyb3V0ZTogcCxcblx0XHRcdFx0XHRzZXJ2ZTogYC8ke3Auc3BsaXQoJy8nKS5maWx0ZXIoQm9vbGVhbikuam9pbignLycpfS9pbmRleC5odG1sYCxcblx0XHRcdFx0XHRzdGF0dXNDb2RlOiAyMDAgYXMgY29uc3QsXG5cdFx0XHRcdH0pKVxuXG5cdFx0XHRjb25zdCBjb25maWc6IEF6dXJlU3RhdGljV2ViQXBwQ29uZmlnID0ge1xuXHRcdFx0XHRyb3V0ZXM6IHN0YXRpY1JvdXRlcyxcblx0XHRcdFx0dHJhaWxpbmdTbGFzaDogJ2F1dG8nLFxuXHRcdFx0XHRyZXNwb25zZU92ZXJyaWRlczoge1xuXHRcdFx0XHRcdDQwNDogeyByZXdyaXRlOiAnLzQwNC5odG1sJywgc3RhdHVzQ29kZTogNDA0IH0sXG5cdFx0XHRcdH0sXG5cdFx0XHR9XG5cblx0XHRcdGF3YWl0IHdyaXRlRmlsZShcblx0XHRcdFx0cGF0aC5qb2luKG91dHB1dERpcmVjdG9yeSwgJ3N0YXRpY3dlYmFwcC5jb25maWcuanNvbicpLFxuXHRcdFx0XHRKU09OLnN0cmluZ2lmeShjb25maWcsIHVuZGVmaW5lZCwgMiksXG5cdFx0XHRcdCd1dGY4Jyxcblx0XHRcdClcblx0XHR9LFxuXHR9KVxufVxuXG50eXBlIEF6dXJlUm91dGUgPSB7IHJvdXRlOiBzdHJpbmc7IHNlcnZlOiBzdHJpbmc7IHN0YXR1c0NvZGU6IDIwMCB9XG5cbnR5cGUgQXp1cmVTdGF0aWNXZWJBcHBDb25maWcgPSB7XG5cdHJvdXRlczogQXp1cmVSb3V0ZVtdXG5cdHRyYWlsaW5nU2xhc2g6ICdhbHdheXMnIHwgJ25ldmVyJyB8ICdhdXRvJ1xuXHRyZXNwb25zZU92ZXJyaWRlczogeyA0MDQ6IHsgcmV3cml0ZTogc3RyaW5nOyBzdGF0dXNDb2RlOiA0MDQgfSB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBK0NBLFNBQWdCLHlCQUF5QixTQUFxRDtDQUM3RixPQUFPLGNBQWM7RUFDcEIsTUFBTTtFQUNOLFFBQVEsU0FBUztFQUNqQixNQUFNLE1BQU0sRUFBRSxpQkFBaUIsa0JBQWtCO0dBU2hELE1BQU0sU0FBa0M7SUFDdkMsUUFUa0MsZUFBZSxZQUNoRCxRQUFPLE1BQUssRUFBRSxNQUFNLEdBQUcsQ0FBQyxDQUFDLE9BQU8sT0FBTyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FDcEQsS0FBSSxPQUFNO0tBQ1YsT0FBTztLQUNQLE9BQU8sSUFBSSxFQUFFLE1BQU0sR0FBRyxDQUFDLENBQUMsT0FBTyxPQUFPLENBQUMsQ0FBQyxLQUFLLEdBQUcsRUFBRTtLQUNsRCxZQUFZO0lBQ2IsRUFHbUI7SUFDbkIsZUFBZTtJQUNmLG1CQUFtQixFQUNsQixLQUFLO0tBQUUsU0FBUztLQUFhLFlBQVk7SUFBSSxFQUM5QztHQUNEO0dBRUEsTUFBTSxVQUNMLEtBQUssS0FBSyxpQkFBaUIsMEJBQTBCLEdBQ3JELEtBQUssVUFBVSxRQUFRLEtBQUEsR0FBVyxDQUFDLEdBQ25DLE1BQ0Q7RUFDRDtDQUNELENBQUM7QUFDRiJ9
125
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp1cmUtc3RhdGljLXdlYmFwcC5tanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL2FkYXB0ZXIvcm91dGVzLm10cyIsIi4uL3NyYy9hZGFwdGVyLm10cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjYXRjaEFsbFByZWZpeGVzLCB3aXRoVHJhaWxpbmdTbGFzaCB9IGZyb20gJ0Byb290ZWQvYWRhcHRlcidcblxuaW1wb3J0IHR5cGUgeyBSZXNvbHZlZEFkYXB0ZXJSb3V0ZXMgfSBmcm9tICdAcm9vdGVkL2FkYXB0ZXInXG5cbi8qKiBPbmUgZW50cnkgaW4gdGhlIGByb3V0ZXNgIGFycmF5IG9mIGBzdGF0aWN3ZWJhcHAuY29uZmlnLmpzb25gLiAqL1xuZXhwb3J0IHR5cGUgQXp1cmVSb3V0ZSA9IHsgcm91dGU6IHN0cmluZzsgcmV3cml0ZT86IHN0cmluZyB9XG5cbmNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gJ3N0YXRpY3dlYmFwcC5jb25maWcuanNvbidcblxuLyoqXG4gKiBCdWlsZHMgdGhlIGByb3V0ZXNgIGFycmF5IGZvciBgc3RhdGljd2ViYXBwLmNvbmZpZy5qc29uYC5cbiAqXG4gKiBBenVyZSBvbmx5IHN1cHBvcnRzIGEgd2lsZGNhcmQgYXQgdGhlIGVuZCBvZiBhIHJvdXRlLCBzbyBhIGR5bmFtaWMgcGF0dGVybiBjYW4ndCBiZSBtYXRjaGVkIHBlciBzZWdtZW50LlxuICogRWFjaCBwYXR0ZXJuIGlzIGN1dCBhdCBpdHMgZmlyc3QgYDpwYXJhbWAgYW5kIGJlY29tZXMgYSBgPHByZWZpeD4qYCBydWxlIHRoYXQgcmV3cml0ZXMgdG8gdGhlIGZhbGxiYWNrIGZpbGUsIHdoaWNoIGFuc3dlcnMgYDIwMGAuXG4gKiBUaGF0IG92ZXItbWF0Y2hlczogYC9yZWNpcGUvOmlkL2AgYmVjb21lcyBgL3JlY2lwZS8qYCwgc28gYC9yZWNpcGUvNDIvZXh0cmEvYCBhbnN3ZXJzIGAyMDBgIHRvbywgYW5kIHRoZSBicm93c2VyLXNpZGUgcm91dGVyIHJlbmRlcnMgbm90LWZvdW5kLlxuICogQSBwYXR0ZXJuIHRoYXQgc3RhcnRzIHdpdGggYSBgOnBhcmFtYCBiZWNvbWVzIGAvKmAsIHdoaWNoIHR1cm5zIGV2ZXJ5IHVua25vd24gcGF0aCBpbnRvIGEgc29mdCA0MDQuXG4gKlxuICogQXp1cmUgYXBwbGllcyByb3V0ZSBydWxlcyBiZWZvcmUgaXQgbG9va3MgZm9yIGEgZmlsZSwgc28gYSB3aWxkY2FyZCB3b3VsZCBhbHNvIHN3YWxsb3cgdGhlIHJlYWwgZmlsZXMgdW5kZXIgaXRzIHByZWZpeC5cbiAqIEV2ZXJ5IG91dHB1dCBmaWxlIGFuZCBwcmUtcmVuZGVyZWQgcGFnZSB1bmRlciBhIHByZWZpeCBnZXRzIGEgcnVsZSB3aXRoIG5vIGFjdGlvbiBhaGVhZCBvZiB0aGUgd2lsZGNhcmRzLiBSdWxlcyBhcmUgZmlyc3QtbWF0Y2gsXG4gKiBzbyB0aGF0IHN0b3BzIGV2YWx1YXRpb24gYW5kIEF6dXJlIHNlcnZlcyB0aGUgZmlsZSBhcyB1c3VhbC4gUGFnZXMgYXJlIGxpc3RlZCBhcyB0aGVpciBgaW5kZXguaHRtbGAsIGJlY2F1c2UgQXp1cmUgbWF0Y2hlcyB0aGF0IHJ1bGUgZm9yIHRoZSBmb2xkZXIgdG9vLlxuICogVGhleSdyZSBwYXNzZWQgaW4gYXMgc3RhdGljIHBhdGhzIHJhdGhlciB0aGFuIGZpbGVzLCBzaW5jZSB0aGUgYWRhcHRlciBydW5zIGJlZm9yZSB0aGUgcGFnZXMgYXJlIHdyaXR0ZW4uXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBidWlsZEF6dXJlUm91dGVzKFxuICogICB7IHN0YXRpY1BhdGhzOiBbJy9yZWNpcGUvbmV3LycsICcvYWJvdXQvJ10sIGR5bmFtaWNQYXR0ZXJuczogWycvcmVjaXBlLzppZC8nLCAnL3JlY2lwZS86aWQvaW5ncmVkaWVudHMvJ10gfSxcbiAqICAgWydyZWNpcGUvcGhvdG8uanBnJywgJ2Fzc2V0cy9pbmRleC5qcyddLFxuICogICAnNDA0Lmh0bWwnLFxuICogKVxuICogLy8gW1xuICogLy8gICB7IHJvdXRlOiAnL3JlY2lwZS9waG90by5qcGcnIH0sXG4gKiAvLyAgIHsgcm91dGU6ICcvcmVjaXBlL25ldy9pbmRleC5odG1sJyB9LFxuICogLy8gICB7IHJvdXRlOiAnL3JlY2lwZS8qJywgcmV3cml0ZTogJy80MDQuaHRtbCcgfSxcbiAqIC8vIF1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRBenVyZVJvdXRlcyhcblx0cm91dGVzOiBSZXNvbHZlZEFkYXB0ZXJSb3V0ZXMsXG5cdG91dHB1dEZpbGVzOiBzdHJpbmdbXSxcblx0ZmFsbGJhY2tGaWxlTmFtZTogc3RyaW5nLFxuKTogQXp1cmVSb3V0ZVtdIHtcblx0Y29uc3QgcHJlZml4ZXMgPSBjYXRjaEFsbFByZWZpeGVzKHJvdXRlcy5keW5hbWljUGF0dGVybnMpXG5cblx0Y29uc3QgZmlsZXMgPSBbXG5cdFx0Li4ub3V0cHV0RmlsZXMubWFwKGZpbGUgPT4gYC8ke2ZpbGV9YCkuZmlsdGVyKGZpbGUgPT4gZmlsZSAhPT0gYC8ke2NvbmZpZ0ZpbGVOYW1lfWApLFxuXHRcdC4uLnJvdXRlcy5zdGF0aWNQYXRocy5tYXAoc3RhdGljUGF0aCA9PiBgJHt3aXRoVHJhaWxpbmdTbGFzaChzdGF0aWNQYXRoKX1pbmRleC5odG1sYCksXG5cdF1cblx0Y29uc3QgcGFzc1Rocm91Z2g6IEF6dXJlUm91dGVbXSA9IFsuLi5uZXcgU2V0KGZpbGVzKV1cblx0XHQuZmlsdGVyKGZpbGUgPT4gcHJlZml4ZXMuc29tZShwcmVmaXggPT4gZmlsZS5zdGFydHNXaXRoKHByZWZpeCkpKVxuXHRcdC5tYXAoZmlsZSA9PiAoeyByb3V0ZTogZmlsZSB9KSlcblxuXHRjb25zdCB3aWxkY2FyZHM6IEF6dXJlUm91dGVbXSA9IHByZWZpeGVzXG5cdFx0Lm1hcChwcmVmaXggPT4gKHsgcm91dGU6IGAke3ByZWZpeH0qYCwgcmV3cml0ZTogYC8ke2ZhbGxiYWNrRmlsZU5hbWV9YCB9KSlcblxuXHRyZXR1cm4gWy4uLnBhc3NUaHJvdWdoLCAuLi53aWxkY2FyZHNdXG59XG4iLCJpbXBvcnQgeyBCdWZmZXIgfSBmcm9tICdub2RlOmJ1ZmZlcidcbmltcG9ydCB7IHJlYWRkaXIsIHdyaXRlRmlsZSB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnXG5cbmltcG9ydCB7IGNhdGNoQWxsUHJlZml4ZXMsIHN0YXRpY0FkYXB0ZXIgfSBmcm9tICdAcm9vdGVkL2FkYXB0ZXInXG5cbmltcG9ydCB7IGJ1aWxkQXp1cmVSb3V0ZXMgfSBmcm9tICcuL2FkYXB0ZXIvcm91dGVzLm10cydcblxuaW1wb3J0IHR5cGUgeyBBenVyZVJvdXRlIH0gZnJvbSAnLi9hZGFwdGVyL3JvdXRlcy5tdHMnXG5pbXBvcnQgdHlwZSB7IEFkYXB0ZXJSb3V0ZXMgfSBmcm9tICdAcm9vdGVkL2FkYXB0ZXInXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gJ3ZpdGUnXG5cbi8qKlxuICogV2hhdCBBenVyZSBhbnN3ZXJzIGZvciBhIGA6cGFyYW1gIHJvdXRlLiBUaGVyZSdzIG5vIGRlZmF1bHQsIGJlY2F1c2UgYm90aCBhcmUgd3Jvbmcgc29tZXdoZXJlLFxuICogYW5kIHdoaWNoIG9uZSBodXJ0cyBsZXNzIGRlcGVuZHMgb24geW91ciBzaXRlLiBTZWUge0BsaW5rIGF6dXJlU3RhdGljV2ViYXBwQWRhcHRlcn0uXG4gKi9cbmV4cG9ydCB0eXBlIEF6dXJlRHluYW1pY1JvdXRlcyA9ICdub3QtZm91bmQnIHwgJ2NhdGNoLWFsbCdcblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyfS5cbiAqL1xuZXhwb3J0IHR5cGUgQXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyT3B0aW9ucyA9IHtcblx0LyoqXG5cdCAqIFdoYXQgQXp1cmUgYW5zd2VycyBmb3IgYSBgOnBhcmFtYCByb3V0ZS4gUmVxdWlyZWQsIHNlZSB7QGxpbmsgYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyfSBmb3IgdGhlIHRyYWRlLW9mZi5cblx0ICpcblx0ICogLSBgJ25vdC1mb3VuZCdgOiByZWFsIGR5bmFtaWMgcGFnZXMgYW5zd2VyIGA0MDRgLiBFdmVyeSBzdGF0dXMgZm9yIGEgcGF0aCB0aGF0IGlzbid0IGEgcm91dGUgaXMgcmlnaHQuXG5cdCAqIC0gYCdjYXRjaC1hbGwnYDogcmVhbCBkeW5hbWljIHBhZ2VzIGFuc3dlciBgMjAwYCwgYW5kIHNvIGRvZXMgYW55dGhpbmcgZGVlcGVyIHVuZGVyIHRoZSBzYW1lIHByZWZpeC5cblx0ICovXG5cdGR5bmFtaWNSb3V0ZXM6IEF6dXJlRHluYW1pY1JvdXRlc1xuXHQvKipcblx0ICogTWFudWFsIHJvdXRlIGxpc3QgZm9yIHByb2plY3RzIHRoYXQgZG9uJ3QgdXNlIGBnZW5lcmF0ZVJvdXRlTWFuaWZlc3RgLlxuXHQgKiBTZWUge0BsaW5rIEFkYXB0ZXJSb3V0ZXN9LlxuXHQgKi9cblx0cm91dGVzPzogQWRhcHRlclJvdXRlc1xufVxuXG4vKipcbiAqIEFkYXB0ZXIgZm9yIEF6dXJlIFN0YXRpYyBXZWIgQXBwcy5cbiAqXG4gKiBXcml0ZXMgYHN0YXRpY3dlYmFwcC5jb25maWcuanNvbmAgdG8gdGhlIG91dHB1dCBkaXJlY3RvcnkuIFByZS1yZW5kZXJlZCBwYWdlcyBhcmUgcGxhaW4gZmlsZXMsIHdoaWNoIEF6dXJlIHNlcnZlcyB3aXRoIGEgYDIwMGAgb24gaXRzIG93bixcbiAqIGFuZCBhIHBhdGggdGhhdCBkb2Vzbid0IHJlc29sdmUgZ2V0cyBgNDA0Lmh0bWxgIHdpdGggYSByZWFsIGA0MDRgLlxuICpcbiAqIEF6dXJlIGNhbid0IG1hdGNoIGEgYDpwYXJhbWAgcm91dGUgZXhhY3RseS4gQSB3aWxkY2FyZCBpcyBvbmx5IGFsbG93ZWQgYXQgdGhlIGVuZCBvZiBhIHJ1bGUgYW5kIHRoZXJlJ3Mgbm8gcGVyLXNlZ21lbnQgbWF0Y2gsXG4gKiBzbyB0aGVyZSdzIG5vIHJ1bGUgdGhhdCBtZWFucyBcImAvcmVjaXBlL2AgcGx1cyBleGFjdGx5IG9uZSBzZWdtZW50XCIuIFlvdSBoYXZlIHRvIHBpY2sgd2hpY2ggd2F5IGl0J3Mgd3Jvbmc6XG4gKlxuICogKipgJ25vdC1mb3VuZCdgKiogd3JpdGVzIG5vIHJ1bGUgZm9yIGR5bmFtaWMgcm91dGVzLlxuICogLSBgL3JlY2lwZS80Mi9gIHNlcnZlcyB0aGUgc2hlbGwgd2l0aCBhIGA0MDRgLiBUaGUgcGFnZSByZW5kZXJzLCBiZWNhdXNlIHRoZSBicm93c2VyLXNpZGUgcm91dGVyIHRha2VzIG92ZXIsIGJ1dCBjcmF3bGVycyBkcm9wIGl0LlxuICogICBJZiB5b3VyIGR5bmFtaWMgcGFnZXMgYXJlIGNvbnRlbnQgeW91IHdhbnQgZm91bmQsIHRoYXQncyBtb3N0IG9mIHlvdXIgc2l0ZSBnb25lIGZyb20gc2VhcmNoLlxuICogLSBFdmVyeXRoaW5nIHRoYXQgaXNuJ3QgYSByb3V0ZSBhbnN3ZXJzIGA0MDRgLCBzbyB0aGVyZSBhcmUgbm8gc29mdCA0MDRzLlxuICpcbiAqICoqYCdjYXRjaC1hbGwnYCoqIHdyaXRlcyBgL3JlY2lwZS8qYCBmb3IgYC9yZWNpcGUvOmlkL2AsIGN1dCBhdCB0aGUgZmlyc3QgYDpwYXJhbWAuXG4gKiAtIGAvcmVjaXBlLzQyL2AgYW5zd2VycyBgMjAwYC5cbiAqIC0gU28gZG9lcyBgL3JlY2lwZS80Mi9leHRyYS9gLCB3aGljaCBpc24ndCBhIHJvdXRlLiBUaGUgYnJvd3Nlci1zaWRlIHJvdXRlciByZW5kZXJzIHlvdXIgbm90LWZvdW5kIHBhZ2Ugd2l0aCBhIGAyMDBgLlxuICogICBOb3RoaW5nIGxpbmtzIHRoZXJlLCBzbyBjcmF3bGVycyByYXJlbHkgc2VlIGl0LCBidXQgaXQncyBhIHNvZnQgNDA0IHdoZW4gdGhleSBkby5cbiAqIC0gQSByb3V0ZSB0aGF0IHN0YXJ0cyB3aXRoIGEgYDpwYXJhbWAsIGxpa2UgYC86c2x1Zy9gLCBiZWNvbWVzIGAvKmA6IGV2ZXJ5IHVua25vd24gcGF0aCBvbiB0aGUgc2l0ZSBhbnN3ZXJzIGAyMDBgLiBUaGUgYnVpbGQgd2FybnMgd2hlbiB0aGF0IGhhcHBlbnMuXG4gKiAtIEF6dXJlIGNoZWNrcyBydWxlcyBiZWZvcmUgZmlsZXMsIHNvIGV2ZXJ5IHJlYWwgZmlsZSB1bmRlciBhIHdpbGRjYXJkIGdldHMgYSBydWxlIG9mIGl0cyBvd24gdG8gc3RheSByZWFjaGFibGUuXG4gKiAgIFdpdGggYC8qYCB0aGF0J3MgZXZlcnkgZmlsZSBpbiB0aGUgYnVpbGQsIGFuZCBBenVyZSBjYXBzIHRoZSBjb25maWcgYXQgMjAgS0IuIFRoZSBidWlsZCBmYWlscyBpZiBpdCBnb2VzIG92ZXIuXG4gKlxuICogYHZpdGUgZGV2YCBhbmQgYHZpdGUgcHJldmlld2AgYW5zd2VyIHRoZSBzYW1lIHdheSBhcyB0aGUgb3B0aW9uIHlvdSBwaWNrLlxuICogTmVpdGhlciBvcHRpb24gZ2l2ZXMgYW4gZXhhY3QgYW5zd2VyLiBGb3IgdGhhdCB5b3UgbmVlZCBzb21ldGhpbmcgdGhhdCBydW5zIHRoZSByb3V0ZSBtYXRjaGluZyBpdHNlbGYsIGxpa2UgYEByb290ZWQtYWRhcHRlcnMvZmFzdGlmeWAuXG4gKlxuICogQGV4YW1wbGUgYHZpdGUuY29uZmlnLnRzYFxuICogYGBgdHNcbiAqIGltcG9ydCB7IHJvb3RlZE1hbmlmZXN0IH0gZnJvbSAnQHJvb3RlZC9hcHBsaWNhdGlvbidcbiAqIGltcG9ydCB7IGdlbmVyYXRlUm91dGVNYW5pZmVzdCB9IGZyb20gJ0Byb290ZWQvcm91dGVyL21hbmlmZXN0J1xuICogaW1wb3J0IHsgYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyIH0gZnJvbSAnQHJvb3RlZC1hZGFwdGVycy9henVyZS1zdGF0aWMtd2ViYXBwJ1xuICpcbiAqIGV4cG9ydCBkZWZhdWx0IHJvb3RlZE1hbmlmZXN0KHtcbiAqICAgcGx1Z2luczogW1xuICogICAgIGdlbmVyYXRlUm91dGVNYW5pZmVzdCh7IGdsb2I6ICcuL3NyYy8qKlxcL19yb3V0ZXMubXRzJywgcm9vdDogJy4vc3JjL19yb3V0ZXMuZy5tdHMnIH0pLFxuICogICAgIC8vIFJlY2lwZSBwYWdlcyBzaG91bGQgYmUgaW5kZXhlZDsgYSAyMDAgb24gL3JlY2lwZS80Mi9leHRyYS8gaXMgdGhlIHByaWNlLlxuICogICAgIGF6dXJlU3RhdGljV2ViYXBwQWRhcHRlcih7IGR5bmFtaWNSb3V0ZXM6ICdjYXRjaC1hbGwnIH0pLFxuICogICBdLFxuICogfSlcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gYXp1cmVTdGF0aWNXZWJhcHBBZGFwdGVyKG9wdGlvbnM6IEF6dXJlU3RhdGljV2ViYXBwQWRhcHRlck9wdGlvbnMpOiBQbHVnaW5bXSB7XG5cdGNvbnN0IGR5bmFtaWNSb3V0ZXMgPSByZXF1aXJlRHluYW1pY1JvdXRlcyhvcHRpb25zKVxuXG5cdHJldHVybiBzdGF0aWNBZGFwdGVyKHtcblx0XHRuYW1lOiAncm9vdGVkOmF6dXJlLXN0YXRpYy13ZWJhcHAnLFxuXHRcdHJvdXRlczogb3B0aW9ucy5yb3V0ZXMsXG5cdFx0ZHluYW1pY1JvdXRlcyxcblx0XHRhc3luYyBzZXR1cCh7IGNvbmZpZywgb3V0cHV0RGlyZWN0b3J5LCByZXNvbHZlZFJvdXRlcyB9KSB7XG5cdFx0XHRjb25zdCByb3V0ZXMgPSBkeW5hbWljUm91dGVzID09PSAnY2F0Y2gtYWxsJ1xuXHRcdFx0XHQ/IGJ1aWxkQXp1cmVSb3V0ZXMocmVzb2x2ZWRSb3V0ZXMsIGF3YWl0IGxpc3RGaWxlcyhvdXRwdXREaXJlY3RvcnkpLCAnNDA0Lmh0bWwnKVxuXHRcdFx0XHQ6IFtdXG5cblx0XHRcdGlmIChkeW5hbWljUm91dGVzID09PSAnY2F0Y2gtYWxsJyAmJiBjYXRjaEFsbFByZWZpeGVzKHJlc29sdmVkUm91dGVzLmR5bmFtaWNQYXR0ZXJucykuaW5jbHVkZXMoJy8nKSkge1xuXHRcdFx0XHRjb25maWcubG9nZ2VyLndhcm4oXG5cdFx0XHRcdFx0J1tyb290ZWQ6YXp1cmUtc3RhdGljLXdlYmFwcF0gYSByb3V0ZSBzdGFydHMgd2l0aCBhIDpwYXJhbSwgc28gdGhlIGNhdGNoLWFsbCBydWxlIGlzIC8qLiAnXG5cdFx0XHRcdFx0KyAnRXZlcnkgdW5rbm93biBwYXRoIG9uIHRoZSBzaXRlIG5vdyBhbnN3ZXJzIDIwMC4nLFxuXHRcdFx0XHQpXG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IGF6dXJlQ29uZmlnOiBBenVyZVN0YXRpY1dlYkFwcENvbmZpZyA9IHtcblx0XHRcdFx0cm91dGVzLFxuXHRcdFx0XHR0cmFpbGluZ1NsYXNoOiAnYXV0bycsXG5cdFx0XHRcdHJlc3BvbnNlT3ZlcnJpZGVzOiB7XG5cdFx0XHRcdFx0NDA0OiB7IHJld3JpdGU6ICcvNDA0Lmh0bWwnLCBzdGF0dXNDb2RlOiA0MDQgfSxcblx0XHRcdFx0fSxcblx0XHRcdH1cblxuXHRcdFx0Y29uc3QgY29udGVudHMgPSBKU09OLnN0cmluZ2lmeShhenVyZUNvbmZpZywgdW5kZWZpbmVkLCAyKVxuXHRcdFx0Y29uc3Qgc2l6ZSA9IEJ1ZmZlci5ieXRlTGVuZ3RoKGNvbnRlbnRzLCAndXRmOCcpXG5cdFx0XHRpZiAoc2l6ZSA+IG1heENvbmZpZ1NpemUpIHtcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRcdGBbcm9vdGVkOmF6dXJlLXN0YXRpYy13ZWJhcHBdIHN0YXRpY3dlYmFwcC5jb25maWcuanNvbiBpcyAke3NpemV9IGJ5dGVzLCBvdmVyIEF6dXJlJ3MgbGltaXQgb2YgJHttYXhDb25maWdTaXplfS4gYFxuXHRcdFx0XHRcdCsgJ0V2ZXJ5IGZpbGUgdW5kZXIgYSBjYXRjaC1hbGwgcHJlZml4IG5lZWRzIGl0cyBvd24gcnVsZSwgJ1xuXHRcdFx0XHRcdCsgJ2FuZCBhIHJvdXRlIHRoYXQgc3RhcnRzIHdpdGggYSA6cGFyYW0gKGxpa2UgLzpzbHVnLykgcHV0cyBldmVyeSBmaWxlIGluIHRoZSBidWlsZCB1bmRlciBpdC4nLFxuXHRcdFx0XHQpXG5cdFx0XHR9XG5cblx0XHRcdGF3YWl0IHdyaXRlRmlsZShwYXRoLmpvaW4ob3V0cHV0RGlyZWN0b3J5LCAnc3RhdGljd2ViYXBwLmNvbmZpZy5qc29uJyksIGNvbnRlbnRzLCAndXRmOCcpXG5cdFx0fSxcblx0fSlcbn1cblxuLy8gQXp1cmUncyBkb2N1bWVudGVkIG1heGltdW0gZm9yIHN0YXRpY3dlYmFwcC5jb25maWcuanNvbi5cbmNvbnN0IG1heENvbmZpZ1NpemUgPSAyMCAqIDEwMjRcblxudHlwZSBBenVyZVN0YXRpY1dlYkFwcENvbmZpZyA9IHtcblx0cm91dGVzOiBBenVyZVJvdXRlW11cblx0dHJhaWxpbmdTbGFzaDogJ2Fsd2F5cycgfCAnbmV2ZXInIHwgJ2F1dG8nXG5cdHJlc3BvbnNlT3ZlcnJpZGVzOiB7IDQwNDogeyByZXdyaXRlOiBzdHJpbmc7IHN0YXR1c0NvZGU6IDQwNCB9IH1cbn1cblxuLy8gVGhlIHR5cGUgYWxyZWFkeSByZXF1aXJlcyBpdCwgdGhpcyBpcyBmb3IgY29uZmlnIHdyaXR0ZW4gaW4gcGxhaW4gSlMuXG5mdW5jdGlvbiByZXF1aXJlRHluYW1pY1JvdXRlcyhvcHRpb25zOiBBenVyZVN0YXRpY1dlYmFwcEFkYXB0ZXJPcHRpb25zIHwgdW5kZWZpbmVkKTogQXp1cmVEeW5hbWljUm91dGVzIHtcblx0Y29uc3QgdmFsdWUgPSBvcHRpb25zPy5keW5hbWljUm91dGVzXG5cdGlmICh2YWx1ZSA9PT0gJ25vdC1mb3VuZCcgfHwgdmFsdWUgPT09ICdjYXRjaC1hbGwnKSByZXR1cm4gdmFsdWVcblx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdGBbcm9vdGVkOmF6dXJlLXN0YXRpYy13ZWJhcHBdIGR5bmFtaWNSb3V0ZXMgaXMgcmVxdWlyZWQsIGFuZCBoYXMgdG8gYmUgJ25vdC1mb3VuZCcgb3IgJ2NhdGNoLWFsbCcgKGdvdCAke0pTT04uc3RyaW5naWZ5KHZhbHVlKX0pLiBgXG5cdFx0KyAnQXp1cmUgY2FuXFwndCBtYXRjaCBhIDpwYXJhbSByb3V0ZSBleGFjdGx5LCBzbyBwaWNrIHdoaWNoIHdheSBpdFxcJ3Mgd3Jvbmc6ICdcblx0XHQrICdcXCdub3QtZm91bmRcXCcgYW5zd2VycyA0MDQgb24gcmVhbCBkeW5hbWljIHBhZ2VzLCBcXCdjYXRjaC1hbGxcXCcgYW5zd2VycyAyMDAgb24gcGF0aHMgYmVsb3cgdGhlbSB0aGF0IGFyZW5cXCd0IHJvdXRlcy4nLFxuXHQpXG59XG5cbi8qKiBFdmVyeSBmaWxlIGluIHRoZSBvdXRwdXQgZGlyZWN0b3J5LCByZWxhdGl2ZSB0byBpdCBhbmQgd2l0aCBgL2Agc2VwYXJhdG9ycy4gKi9cbmFzeW5jIGZ1bmN0aW9uIGxpc3RGaWxlcyhvdXRwdXREaXJlY3Rvcnk6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nW10+IHtcblx0Y29uc3QgZW50cmllcyA9IGF3YWl0IHJlYWRkaXIob3V0cHV0RGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KVxuXHRyZXR1cm4gZW50cmllc1xuXHRcdC5maWx0ZXIoZW50cnkgPT4gZW50cnkuaXNGaWxlKCkpXG5cdFx0Lm1hcChlbnRyeSA9PiBwYXRoLnJlbGF0aXZlKG91dHB1dERpcmVjdG9yeSwgcGF0aC5qb2luKGVudHJ5LnBhcmVudFBhdGgsIGVudHJ5Lm5hbWUpKS5zcGxpdChwYXRoLnNlcCkuam9pbignLycpKVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7OztBQU9BLE1BQU0saUJBQWlCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBNkJ2QixTQUFnQixpQkFDZixRQUNBLGFBQ0Esa0JBQ2U7Q0FDZixNQUFNLFdBQVcsaUJBQWlCLE9BQU8sZUFBZTtDQUV4RCxNQUFNLFFBQVEsQ0FDYixHQUFHLFlBQVksS0FBSSxTQUFRLElBQUksTUFBTSxDQUFDLENBQUMsUUFBTyxTQUFRLFNBQVMsSUFBSSxnQkFBZ0IsR0FDbkYsR0FBRyxPQUFPLFlBQVksS0FBSSxlQUFjLEdBQUcsa0JBQWtCLFVBQVUsRUFBRSxXQUFXLENBQ3JGO0NBQ0EsTUFBTSxjQUE0QixDQUFDLEdBQUcsSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQ25ELFFBQU8sU0FBUSxTQUFTLE1BQUssV0FBVSxLQUFLLFdBQVcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUNoRSxLQUFJLFVBQVMsRUFBRSxPQUFPLEtBQUssRUFBRTtDQUUvQixNQUFNLFlBQTBCLFNBQzlCLEtBQUksWUFBVztFQUFFLE9BQU8sR0FBRyxPQUFPO0VBQUksU0FBUyxJQUFJO0NBQW1CLEVBQUU7Q0FFMUUsT0FBTyxDQUFDLEdBQUcsYUFBYSxHQUFHLFNBQVM7QUFDckM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNxQkEsU0FBZ0IseUJBQXlCLFNBQW9EO0NBQzVGLE1BQU0sZ0JBQWdCLHFCQUFxQixPQUFPO0NBRWxELE9BQU8sY0FBYztFQUNwQixNQUFNO0VBQ04sUUFBUSxRQUFRO0VBQ2hCO0VBQ0EsTUFBTSxNQUFNLEVBQUUsUUFBUSxpQkFBaUIsa0JBQWtCO0dBQ3hELE1BQU0sU0FBUyxrQkFBa0IsY0FDOUIsaUJBQWlCLGdCQUFnQixNQUFNLFVBQVUsZUFBZSxHQUFHLFVBQVUsSUFDN0UsQ0FBQztHQUVKLElBQUksa0JBQWtCLGVBQWUsaUJBQWlCLGVBQWUsZUFBZSxDQUFDLENBQUMsU0FBUyxHQUFHLEdBQ2pHLE9BQU8sT0FBTyxLQUNiLHlJQUVEO0dBV0QsTUFBTSxXQUFXLEtBQUssVUFBVTtJQVAvQjtJQUNBLGVBQWU7SUFDZixtQkFBbUIsRUFDbEIsS0FBSztLQUFFLFNBQVM7S0FBYSxZQUFZO0lBQUksRUFDOUM7R0FHeUMsR0FBRyxLQUFBLEdBQVcsQ0FBQztHQUN6RCxNQUFNLE9BQU8sT0FBTyxXQUFXLFVBQVUsTUFBTTtHQUMvQyxJQUFJLE9BQU8sZUFDVixNQUFNLElBQUksTUFDVCw0REFBNEQsS0FBSyxnQ0FBZ0MsY0FBYyxzSkFHaEg7R0FHRCxNQUFNLFVBQVUsS0FBSyxLQUFLLGlCQUFpQiwwQkFBMEIsR0FBRyxVQUFVLE1BQU07RUFDekY7Q0FDRCxDQUFDO0FBQ0Y7QUFHQSxNQUFNLGdCQUFnQjtBQVN0QixTQUFTLHFCQUFxQixTQUEwRTtDQUN2RyxNQUFNLFFBQVEsU0FBUztDQUN2QixJQUFJLFVBQVUsZUFBZSxVQUFVLGFBQWEsT0FBTztDQUMzRCxNQUFNLElBQUksTUFDVCx5R0FBeUcsS0FBSyxVQUFVLEtBQUssRUFBRSwwTEFHaEk7QUFDRDs7QUFHQSxlQUFlLFVBQVUsaUJBQTRDO0NBRXBFLFFBQU8sTUFEZSxRQUFRLGlCQUFpQjtFQUFFLFdBQVc7RUFBTSxlQUFlO0NBQUssQ0FBQyxFQUFBLENBRXJGLFFBQU8sVUFBUyxNQUFNLE9BQU8sQ0FBQyxDQUFDLENBQy9CLEtBQUksVUFBUyxLQUFLLFNBQVMsaUJBQWlCLEtBQUssS0FBSyxNQUFNLFlBQVksTUFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQztBQUNqSCJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rooted-adapters/azure-static-webapp",
3
- "version": "1.0.0-alpha.25",
3
+ "version": "1.0.0-alpha.27",
4
4
  "description": "Adapter for Azure Static Web Apps",
5
5
  "keywords": [
6
6
  "web-components",
@@ -37,11 +37,11 @@
37
37
  }
38
38
  },
39
39
  "peerDependencies": {
40
- "@rooted/adapter": "1.0.0-alpha.24",
40
+ "@rooted/adapter": "1.0.0-alpha.26",
41
41
  "vite": ">= 8.0.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@rooted/adapter": "1.0.0-alpha.24",
44
+ "@rooted/adapter": "1.0.0-alpha.26",
45
45
  "@rooted/development": "workspace:*",
46
46
  "@rooted/tsdown": "workspace:^",
47
47
  "@types/node": "^26.6.1",
package/readme.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Deployment adapter for Azure Static Web Apps. Part of the [`@rooted/*`](https://github.com/Marvin-Brouwer/rooted#rooted) framework.
4
4
 
5
- Writes `staticwebapp.config.json` to the output directory with explicit route rules for pre-rendered paths and parameterized routes.
5
+ Writes `staticwebapp.config.json` to the output directory.
6
+
7
+ Azure can't match a `:param` route exactly, so you have to pick how it's wrong with the required `dynamicRoutes` option.
8
+ `'not-found'` answers `404` on real dynamic pages. `'catch-all'` answers `200` on them, and also on paths below them that aren't routes.
9
+ The [adapters guide](https://github.com/Marvin-Brouwer/rooted/blob/main/docs/guide/adapters.md#azure-static-web-apps-pick-one) has the full trade-off.
6
10
 
7
11
  > [!IMPORTANT]
8
12
  > This package is still in alpha.
@@ -20,7 +24,7 @@ import { azureStaticWebappAdapter } from '@rooted-adapters/azure-static-webapp'
20
24
  export default rootedManifest({
21
25
  plugins: [
22
26
  generateRouteManifest({ glob: './src/**/_routes.mts', routeManifestPath: './src/_routes.g.mts' }),
23
- azureStaticWebappAdapter(),
27
+ azureStaticWebappAdapter({ dynamicRoutes: 'catch-all' }),
24
28
  ],
25
29
  })
26
30
  ```