@fairgarden/monolith 0.1.0-alpha.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/Readme.md +84 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +160 -0
- package/dist/cli.js.map +1 -0
- package/dist/compat.d.ts +4 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +156 -0
- package/dist/compat.js.map +1 -0
- package/dist/eslint.d.ts +62 -0
- package/dist/eslint.d.ts.map +1 -0
- package/dist/eslint.js +198 -0
- package/dist/eslint.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +24 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +473 -0
- package/dist/link.js.map +1 -0
- package/dist/mounts.d.ts +37 -0
- package/dist/mounts.d.ts.map +1 -0
- package/dist/mounts.js +51 -0
- package/dist/mounts.js.map +1 -0
- package/dist/package-json.d.ts +38 -0
- package/dist/package-json.d.ts.map +1 -0
- package/dist/package-json.js +156 -0
- package/dist/package-json.js.map +1 -0
- package/dist/portability.d.ts +36 -0
- package/dist/portability.d.ts.map +1 -0
- package/dist/portability.js +158 -0
- package/dist/portability.js.map +1 -0
- package/dist/portable-link.d.ts +22 -0
- package/dist/portable-link.d.ts.map +1 -0
- package/dist/portable-link.js +24 -0
- package/dist/portable-link.js.map +1 -0
- package/dist/resolve.d.ts +5 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +181 -0
- package/dist/resolve.js.map +1 -0
- package/dist/routes.d.ts +23 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +95 -0
- package/dist/routes.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +74 -0
package/dist/eslint.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
/** Directory names Next treats as a route tree. */
|
|
5
|
+
const ROUTE_DIRS = ['app', 'pages'];
|
|
6
|
+
const filenameOf = (context) => context.filename ?? context.getFilename?.() ?? '';
|
|
7
|
+
const packageRootCache = new Map();
|
|
8
|
+
/** Nearest directory above `from` holding a package.json. */
|
|
9
|
+
const packageRoot = (from) => {
|
|
10
|
+
const cached = packageRootCache.get(from);
|
|
11
|
+
if (cached !== undefined || packageRootCache.has(from))
|
|
12
|
+
return cached;
|
|
13
|
+
let current = from;
|
|
14
|
+
let found;
|
|
15
|
+
for (;;) {
|
|
16
|
+
if (existsSync(path.join(current, 'package.json'))) {
|
|
17
|
+
found = current;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
const parent = path.dirname(current);
|
|
21
|
+
if (parent === current)
|
|
22
|
+
break;
|
|
23
|
+
current = parent;
|
|
24
|
+
}
|
|
25
|
+
packageRootCache.set(from, found);
|
|
26
|
+
return found;
|
|
27
|
+
};
|
|
28
|
+
const packageNameOf = (root) => {
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
31
|
+
return typeof pkg.name === 'string' ? pkg.name : undefined;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The route tree this file belongs to, if any.
|
|
39
|
+
*
|
|
40
|
+
* Looks for `app/` or `pages/` directly under the package root, or under
|
|
41
|
+
* `src/`, which is where Next looks too.
|
|
42
|
+
*/
|
|
43
|
+
const routeRootOf = (filename, root) => {
|
|
44
|
+
const relative = path.relative(root, filename);
|
|
45
|
+
if (relative.startsWith('..'))
|
|
46
|
+
return undefined;
|
|
47
|
+
const segments = relative.split(path.sep);
|
|
48
|
+
const [first, second] = segments;
|
|
49
|
+
if (ROUTE_DIRS.includes(first))
|
|
50
|
+
return path.join(root, first);
|
|
51
|
+
if (first === 'src' && ROUTE_DIRS.includes(second)) {
|
|
52
|
+
return path.join(root, 'src', second);
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
};
|
|
56
|
+
const isRelative = (request) => request === '.' || request === '..' || /^\.\.?\//.test(request);
|
|
57
|
+
const sourcesOf = (node) => {
|
|
58
|
+
if (node.source)
|
|
59
|
+
return [node.source];
|
|
60
|
+
// `import()` is an ImportExpression, whose argument list is `arguments`;
|
|
61
|
+
// `require()` is a CallExpression whose callee is the identifier.
|
|
62
|
+
if (node.type === 'ImportExpression' && node.arguments)
|
|
63
|
+
return node.arguments;
|
|
64
|
+
if (node.type === 'CallExpression' &&
|
|
65
|
+
node.callee?.type === 'Identifier' &&
|
|
66
|
+
node.callee.name === 'require' &&
|
|
67
|
+
node.arguments) {
|
|
68
|
+
return node.arguments;
|
|
69
|
+
}
|
|
70
|
+
return [];
|
|
71
|
+
};
|
|
72
|
+
const escapingImports = {
|
|
73
|
+
meta: {
|
|
74
|
+
type: 'problem',
|
|
75
|
+
docs: {
|
|
76
|
+
description: 'Disallow relative imports that reach outside a route tree, which do not survive being mounted in a monolith',
|
|
77
|
+
},
|
|
78
|
+
fixable: 'code',
|
|
79
|
+
schema: [
|
|
80
|
+
{
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: { packageName: { type: 'string' } },
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
create(context) {
|
|
88
|
+
const filename = filenameOf(context);
|
|
89
|
+
const root = packageRoot(path.dirname(filename));
|
|
90
|
+
if (!root)
|
|
91
|
+
return {};
|
|
92
|
+
const routeRoot = routeRootOf(filename, root);
|
|
93
|
+
if (!routeRoot)
|
|
94
|
+
return {};
|
|
95
|
+
const option = (context.options[0] ?? {});
|
|
96
|
+
const packageName = option.packageName ?? packageNameOf(root);
|
|
97
|
+
const check = (node) => {
|
|
98
|
+
for (const source of sourcesOf(node)) {
|
|
99
|
+
const request = source.value;
|
|
100
|
+
if (typeof request !== 'string' || !isRelative(request))
|
|
101
|
+
continue;
|
|
102
|
+
const resolved = path.resolve(path.dirname(filename), request);
|
|
103
|
+
const inside = path.relative(routeRoot, resolved);
|
|
104
|
+
if (!inside.startsWith('..'))
|
|
105
|
+
continue;
|
|
106
|
+
const fromRoot = path.relative(root, resolved).split(path.sep).join('/');
|
|
107
|
+
// An import that climbs out of the package has no package-name form;
|
|
108
|
+
// offering one would fix working code into `@scope/name/../outside`.
|
|
109
|
+
const insidePackage = !fromRoot.startsWith('..');
|
|
110
|
+
const replacement = packageName && insidePackage ? `${packageName}/${fromRoot}` : undefined;
|
|
111
|
+
context.report({
|
|
112
|
+
node: source,
|
|
113
|
+
message: replacement
|
|
114
|
+
? `Relative import reaches outside ${path.basename(routeRoot)}/ and breaks once this app is mounted. Use '${replacement}'.`
|
|
115
|
+
: `Relative import reaches outside ${path.basename(routeRoot)}/ and breaks once this app is mounted. Import it by package name.`,
|
|
116
|
+
...(replacement && source.range
|
|
117
|
+
? {
|
|
118
|
+
fix: (fixer) => fixer.replaceText(source, `'${replacement}'`),
|
|
119
|
+
}
|
|
120
|
+
: {}),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
ImportDeclaration: check,
|
|
126
|
+
ExportNamedDeclaration: check,
|
|
127
|
+
ExportAllDeclaration: check,
|
|
128
|
+
ImportExpression: check,
|
|
129
|
+
CallExpression: check,
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
const nextLink = {
|
|
134
|
+
meta: {
|
|
135
|
+
type: 'problem',
|
|
136
|
+
docs: {
|
|
137
|
+
description: 'Disallow next/link in route trees, whose hrefs are not prefixed when the app is mounted',
|
|
138
|
+
},
|
|
139
|
+
schema: [
|
|
140
|
+
{
|
|
141
|
+
type: 'object',
|
|
142
|
+
properties: { replacement: { type: 'string' } },
|
|
143
|
+
additionalProperties: false,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
create(context) {
|
|
148
|
+
const filename = filenameOf(context);
|
|
149
|
+
const root = packageRoot(path.dirname(filename));
|
|
150
|
+
if (!root || !routeRootOf(filename, root))
|
|
151
|
+
return {};
|
|
152
|
+
const option = (context.options[0] ?? {});
|
|
153
|
+
const check = (node) => {
|
|
154
|
+
for (const source of sourcesOf(node)) {
|
|
155
|
+
if (source.value !== 'next/link')
|
|
156
|
+
continue;
|
|
157
|
+
context.report({
|
|
158
|
+
node: source,
|
|
159
|
+
message: option.replacement
|
|
160
|
+
? `next/link does not prefix hrefs with the app's mount point. Use '${option.replacement}'.`
|
|
161
|
+
: "next/link does not prefix hrefs with the app's mount point. Use a Link built with createLink() from '@fairgarden/monolith/link'.",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return { ImportDeclaration: check, ImportExpression: check };
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
export const rules = {
|
|
169
|
+
'no-escaping-relative-imports': escapingImports,
|
|
170
|
+
'no-next-link': nextLink,
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* ESLint plugin keeping an app's routes mountable.
|
|
174
|
+
*
|
|
175
|
+
* ```js
|
|
176
|
+
* // eslint.config.mjs
|
|
177
|
+
* import monolith from '@fairgarden/monolith/eslint'
|
|
178
|
+
*
|
|
179
|
+
* export default [...monolith.configs.recommended]
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
const plugin = {
|
|
183
|
+
meta: { name: '@fairgarden/monolith' },
|
|
184
|
+
rules,
|
|
185
|
+
configs: {},
|
|
186
|
+
};
|
|
187
|
+
plugin.configs.recommended = [
|
|
188
|
+
{
|
|
189
|
+
name: '@fairgarden/monolith/recommended',
|
|
190
|
+
plugins: { '@fairgarden/monolith': plugin },
|
|
191
|
+
rules: {
|
|
192
|
+
'@fairgarden/monolith/no-escaping-relative-imports': 'error',
|
|
193
|
+
'@fairgarden/monolith/no-next-link': 'warn',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
export default plugin;
|
|
198
|
+
//# sourceMappingURL=eslint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../src/eslint.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
import type { MonolithApps, MonolithOptions, ResolvedApp } from './types.ts';
|
|
3
|
+
export type { LinkStrategy, MonolithApp, MonolithApps, MonolithOptions, ResolvedApp, } from './types.ts';
|
|
4
|
+
export { prefixPath } from './routes.ts';
|
|
5
|
+
export { MOUNTS_ENV } from './mounts.ts';
|
|
6
|
+
export { withMonolithicPortability, findPortabilityProblems, type PortabilityOptions, } from './portability.ts';
|
|
7
|
+
export declare const getRegisteredApps: () => ResolvedApp[];
|
|
8
|
+
/**
|
|
9
|
+
* Compose several Next.js apps into one deployable app.
|
|
10
|
+
*
|
|
11
|
+
* Each app's routes are mounted at `/<name>` and its assets at
|
|
12
|
+
* `public/<name>`, and its rewrites, redirects and headers are merged into the
|
|
13
|
+
* monolith's with that same prefix.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* export default withMonolith(
|
|
17
|
+
* { reactStrictMode: true },
|
|
18
|
+
* { id: '@fairgarden/id', membership: '@fairgarden/membership' }
|
|
19
|
+
* )
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Apps may live in the surrounding monorepo or be plain dependencies. Because
|
|
23
|
+
* `basePath` is not used, links inside an app are not prefixed automatically —
|
|
24
|
+
* see the package README.
|
|
25
|
+
*/
|
|
26
|
+
export declare const withMonolith: (nextConfig?: NextConfig, apps?: MonolithApps, options?: MonolithOptions) => ((phase: string) => Promise<NextConfig>);
|
|
27
|
+
export default withMonolith;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAMtC,OAAO,KAAK,EAEV,YAAY,EACZ,eAAe,EACf,WAAW,EACZ,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAA;AAazB,eAAO,MAAM,iBAAiB,QAAO,WAAW,EAAgB,CAAA;AAehE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,GACvB,aAAY,UAAe,EAC3B,OAAM,YAAiB,EACvB,UAAS,eAAoB,KAC5B,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAwCzC,CAAA;AAED,eAAe,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { assertCompatible } from "./compat.js";
|
|
3
|
+
import { linkApps } from "./link.js";
|
|
4
|
+
import { resolveApps } from "./resolve.js";
|
|
5
|
+
import { mergeHeaders, mergeRedirects, mergeRewrites } from "./routes.js";
|
|
6
|
+
import { MOUNTS_ENV } from "./mounts.js";
|
|
7
|
+
export { prefixPath } from "./routes.js";
|
|
8
|
+
export { MOUNTS_ENV } from "./mounts.js";
|
|
9
|
+
export { withMonolithicPortability, findPortabilityProblems, } from "./portability.js";
|
|
10
|
+
const PHASE_DEVELOPMENT_SERVER = 'phase-development-server';
|
|
11
|
+
/**
|
|
12
|
+
* Set by the CLI, which loads the monolith's config only to discover which apps
|
|
13
|
+
* it composes and must not touch the file tree while doing so.
|
|
14
|
+
*/
|
|
15
|
+
const skipLinking = () => process.env.FG_MONOLITH_SKIP_LINK === '1';
|
|
16
|
+
/** Apps seen by the most recent `withMonolith` evaluation, for the CLI. */
|
|
17
|
+
let registered = [];
|
|
18
|
+
export const getRegisteredApps = () => registered;
|
|
19
|
+
/**
|
|
20
|
+
* `next build` follows the committed symlinks, so it reads the source tree as
|
|
21
|
+
* it stands. `next dev` does not discover routes through a symlinked
|
|
22
|
+
* directory, so it gets a derived tree with the files symlinked individually.
|
|
23
|
+
*/
|
|
24
|
+
const pickStrategy = (strategy, phase) => {
|
|
25
|
+
if (strategy && strategy !== 'auto')
|
|
26
|
+
return strategy;
|
|
27
|
+
return phase === PHASE_DEVELOPMENT_SERVER ? 'mirror' : 'source';
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Compose several Next.js apps into one deployable app.
|
|
31
|
+
*
|
|
32
|
+
* Each app's routes are mounted at `/<name>` and its assets at
|
|
33
|
+
* `public/<name>`, and its rewrites, redirects and headers are merged into the
|
|
34
|
+
* monolith's with that same prefix.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* export default withMonolith(
|
|
38
|
+
* { reactStrictMode: true },
|
|
39
|
+
* { id: '@fairgarden/id', membership: '@fairgarden/membership' }
|
|
40
|
+
* )
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Apps may live in the surrounding monorepo or be plain dependencies. Because
|
|
44
|
+
* `basePath` is not used, links inside an app are not prefixed automatically —
|
|
45
|
+
* see the package README.
|
|
46
|
+
*/
|
|
47
|
+
export const withMonolith = (nextConfig = {}, apps = {}, options = {}) => {
|
|
48
|
+
return async (phase) => {
|
|
49
|
+
const root = options.root ?? process.cwd();
|
|
50
|
+
const resolved = await resolveApps(root, apps, phase);
|
|
51
|
+
assertCompatible(nextConfig, resolved, root);
|
|
52
|
+
registered = resolved;
|
|
53
|
+
if (!skipLinking()) {
|
|
54
|
+
const strategy = pickStrategy(options.strategy, phase);
|
|
55
|
+
const sourceDir = path.resolve(root, options.sourceDir ?? path.join('src', 'app'));
|
|
56
|
+
await linkApps(root, resolved, {
|
|
57
|
+
strategy,
|
|
58
|
+
sourceDir,
|
|
59
|
+
sourcePagesDir: path.join(path.dirname(sourceDir), 'pages'),
|
|
60
|
+
selfReference: options.selfReference,
|
|
61
|
+
// Only needed to notice routes being added or removed.
|
|
62
|
+
watch: options.watch ??
|
|
63
|
+
(strategy !== 'source' && phase === PHASE_DEVELOPMENT_SERVER),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const mounts = Object.fromEntries(resolved.flatMap((app) => app.packageName ? [[app.packageName, app.prefix]] : []));
|
|
67
|
+
return {
|
|
68
|
+
...nextConfig,
|
|
69
|
+
env: { ...nextConfig.env, [MOUNTS_ENV]: JSON.stringify(mounts) },
|
|
70
|
+
rewrites: await mergeRewrites(nextConfig, resolved),
|
|
71
|
+
redirects: await mergeRedirects(nextConfig, resolved),
|
|
72
|
+
headers: await mergeHeaders(nextConfig, resolved),
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
export default withMonolith;
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LinkStrategy, ResolvedApp } from './types.ts';
|
|
2
|
+
export interface LinkOptions {
|
|
3
|
+
strategy: LinkStrategy;
|
|
4
|
+
watch: boolean;
|
|
5
|
+
/** Committed App Router directory, absolute. */
|
|
6
|
+
sourceDir: string;
|
|
7
|
+
/** Committed Pages Router directory, absolute. */
|
|
8
|
+
sourcePagesDir: string;
|
|
9
|
+
selfReference?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Make an app resolvable by its own package name from inside its own tree.
|
|
13
|
+
*
|
|
14
|
+
* Apps import their own modules by package name, because relative imports stop
|
|
15
|
+
* working once the routes are mounted elsewhere. Turbopack resolves a mounted
|
|
16
|
+
* file from its real location, so the monolith's `node_modules` is not on the
|
|
17
|
+
* lookup path and the app has to be able to find itself.
|
|
18
|
+
*
|
|
19
|
+
* Leaves a real installed package alone, and only replaces a symlink it would
|
|
20
|
+
* have created itself.
|
|
21
|
+
*/
|
|
22
|
+
export declare const linkSelfReference: (app: ResolvedApp) => Promise<void>;
|
|
23
|
+
export declare const linkApps: (monolithRoot: string, apps: ResolvedApp[], options: LinkOptions) => Promise<string[]>;
|
|
24
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,YAAY,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAmND;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAU,KAAK,WAAW,KAAG,OAAO,CAAC,IAAI,CActE,CAAA;AAwND,eAAO,MAAM,QAAQ,GACnB,cAAc,MAAM,EACpB,MAAM,WAAW,EAAE,EACnB,SAAS,WAAW,KACnB,OAAO,CAAC,MAAM,EAAE,CAiJlB,CAAA"}
|