@equinor/fusion-framework-app 13.0.0 → 14.0.0-next.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +35 -65
  3. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js +70 -0
  4. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js.map +1 -0
  5. package/dist/esm/__tests__/mock/mock-app.test.js +86 -0
  6. package/dist/esm/__tests__/mock/mock-app.test.js.map +1 -0
  7. package/dist/esm/__tests__/mock/msal-hoisting.test.js +36 -0
  8. package/dist/esm/__tests__/mock/msal-hoisting.test.js.map +1 -0
  9. package/dist/esm/configure-modules.js +2 -42
  10. package/dist/esm/configure-modules.js.map +1 -1
  11. package/dist/esm/initialize-app-modules.js +65 -0
  12. package/dist/esm/initialize-app-modules.js.map +1 -0
  13. package/dist/esm/mock/AppMockConfigurator.js +183 -0
  14. package/dist/esm/mock/AppMockConfigurator.js.map +1 -0
  15. package/dist/esm/mock/enable-app-manifest-mock.js +49 -0
  16. package/dist/esm/mock/enable-app-manifest-mock.js.map +1 -0
  17. package/dist/esm/mock/index.js +21 -0
  18. package/dist/esm/mock/index.js.map +1 -0
  19. package/dist/esm/mock/mock-app-modules.js +82 -0
  20. package/dist/esm/mock/mock-app-modules.js.map +1 -0
  21. package/dist/esm/version.js +1 -1
  22. package/dist/esm/version.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/types/__tests__/mock/AppMockConfigurator.test.d.ts +1 -0
  25. package/dist/types/__tests__/mock/mock-app.test.d.ts +1 -0
  26. package/dist/types/__tests__/mock/msal-hoisting.test.d.ts +1 -0
  27. package/dist/types/initialize-app-modules.d.ts +31 -0
  28. package/dist/types/mock/AppMockConfigurator.d.ts +142 -0
  29. package/dist/types/mock/enable-app-manifest-mock.d.ts +33 -0
  30. package/dist/types/mock/index.d.ts +20 -0
  31. package/dist/types/mock/mock-app-modules.d.ts +72 -0
  32. package/dist/types/version.d.ts +1 -1
  33. package/docs/bookmarks.md +18 -0
  34. package/docs/http-clients.md +71 -0
  35. package/docs/testing.md +105 -0
  36. package/package.json +22 -13
  37. package/src/__tests__/mock/AppMockConfigurator.test.ts +96 -0
  38. package/src/__tests__/mock/mock-app.test.ts +112 -0
  39. package/src/__tests__/mock/msal-hoisting.test.ts +54 -0
  40. package/src/configure-modules.ts +2 -52
  41. package/src/initialize-app-modules.ts +98 -0
  42. package/src/mock/AppMockConfigurator.ts +218 -0
  43. package/src/mock/enable-app-manifest-mock.ts +62 -0
  44. package/src/mock/index.ts +21 -0
  45. package/src/mock/mock-app-modules.ts +109 -0
  46. package/src/version.ts +1 -1
  47. package/vitest.config.ts +1 -1
@@ -0,0 +1,183 @@
1
+ import http from '@equinor/fusion-framework-module-http';
2
+ import { msalMockModule, } from '@equinor/fusion-framework-module-msal/mock';
3
+ import { AppConfigurator } from '../AppConfigurator.js';
4
+ /**
5
+ * The real `AppConfigurator`, with the `msal` module it registers backed by
6
+ * the same test double `FrameworkMockConfigurator` uses. `http` is the real
7
+ * module — fake a response by registering a short-circuiting middleware
8
+ * through `.http.addMiddleware(...)` instead of swapping the module out.
9
+ *
10
+ * @remarks
11
+ * Nothing else changes: the same module set (`event`, `http`, `msal`), the same
12
+ * configuration pipeline and the same lifecycle are used. `configureHttpClient`,
13
+ * `useFrameworkServiceClient` and any callback written for a real
14
+ * `AppConfigurator` work against this unchanged.
15
+ *
16
+ * `http` and `msal` are pinned early — mirroring `FrameworkMockConfigurator`,
17
+ * one level down — so `.http` and `.msal` are reachable synchronously, before
18
+ * `useFrameworkServiceClient` or a `configureModules` callback ever runs.
19
+ * `event` is deliberately not pinned, for the same reason it isn't in
20
+ * `FrameworkMockConfigurator`: its `configure` factory reads `ref` to wire
21
+ * bubbling to a parent event provider, and pinning would freeze that decision
22
+ * before a `ref` could ever be known.
23
+ *
24
+ * @typeParam TModules - Module descriptors beyond the default set. Supply this
25
+ * when a test registers application modules, so they are typed on the result.
26
+ * @typeParam TRef - The resolved Fusion modules instance used as a reference during initialization.
27
+ * @typeParam TEnv - The application environment descriptor.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
32
+ * const configurator = new AppMockConfigurator({ manifest });
33
+ *
34
+ * configurator.configureHttpClient('catalog', { baseUri: 'https://api.example.com' });
35
+ * configurator.http.addMiddleware(async (uri, init, next) =>
36
+ * uri === 'https://api.example.com/items' ? Response.json([{ id: 1 }]) : next(uri, init),
37
+ * );
38
+ * ```
39
+ */
40
+ export class AppMockConfigurator extends AppConfigurator {
41
+ static className = 'AppMockConfigurator';
42
+ // Keyed by module name, so `_getConfig` can look a pinned configurator up
43
+ // without needing the module descriptor again.
44
+ #configurators = new Map();
45
+ // Keyed by module name, so `addConfig` can redirect a registration at the
46
+ // pinned descriptor instead of the unpinned one it was given.
47
+ #pinnedModules = new Map();
48
+ /**
49
+ * Creates an app configurator backed by the built-in mock modules.
50
+ *
51
+ * @param env - The application environment containing manifest, config, and optional basename.
52
+ */
53
+ constructor(env) {
54
+ super(env);
55
+ // Pinning up front replaces the modules AppConfigurator's own constructor
56
+ // already registered, whether or not a test ever touches the accessor.
57
+ this._pin(http);
58
+ this._pin(msalMockModule);
59
+ // deferred from AppConfigurator's own constructor (see the override below) until
60
+ // after pinning, so endpoint-derived clients register against the pinned http module
61
+ super._configureHttpClientsFromAppConfig();
62
+ }
63
+ /**
64
+ * No-ops the base constructor's own call to this, since it would otherwise run
65
+ * before {@link _pin} has anything to redirect `addConfig` at; this class calls
66
+ * {@link AppConfigurator._configureHttpClientsFromAppConfig} itself once pinned.
67
+ */
68
+ _configureHttpClientsFromAppConfig() { }
69
+ /**
70
+ * Registers a module configurator, redirecting registrations for a pinned module
71
+ * at its pinned descriptor.
72
+ *
73
+ * @remarks
74
+ * `configureHttpClient`, `useFrameworkServiceClient` and similar helpers always
75
+ * pass the real, unpinned module descriptor — the base `addConfig` replaces a
76
+ * module's descriptor whenever it doesn't recognize the object it's given, even
77
+ * under the same name, which would otherwise silently un-pin it.
78
+ *
79
+ * @param config - The module configurator descriptor to register.
80
+ * @template T - The module type being configured.
81
+ * @template TConfig - The resolved configuration type for the module.
82
+ */
83
+ addConfig(config) {
84
+ const pinnedModule = this.#pinnedModules.get(config.module.name);
85
+ super.addConfig(pinnedModule ? { ...config, module: pinnedModule } : config);
86
+ }
87
+ /**
88
+ * Pins a module to a single configurator instance for the lifetime of this
89
+ * configurator, so it can be reached by name through {@link _getConfig}.
90
+ *
91
+ * @remarks
92
+ * The module system otherwise builds a fresh configurator from its own
93
+ * `configure` factory during the configure phase — too late for a test to
94
+ * reach, and a new instance on every call besides. This replaces that factory
95
+ * with one that always returns the same instance, and registers the result
96
+ * under the module's own name.
97
+ *
98
+ * An application module supplied through {@link TModules} uses this the same
99
+ * way `.http` and `.msal` do, to expose its own named accessor:
100
+ *
101
+ * ```typescript
102
+ * class MyAppMockConfigurator extends AppMockConfigurator<[WidgetsModule]> {
103
+ * constructor(env: AppEnv) {
104
+ * super(env);
105
+ * this._pin(widgetsMockModule);
106
+ * }
107
+ *
108
+ * public get widgets(): WidgetsMockConfigurator {
109
+ * return this._getConfig('widgets');
110
+ * }
111
+ * }
112
+ * ```
113
+ *
114
+ * @param module - The module descriptor to pin a configurator for.
115
+ * @template TModule - The specific module descriptor type being pinned.
116
+ * @throws {Error} If the module declares no `configure` factory to pin, or
117
+ * the factory returns a promise instead of a configurator — pinning is
118
+ * synchronous, so a test can reach the accessor immediately.
119
+ */
120
+ _pin(module) {
121
+ // A module without a configure factory has nothing this method could pin
122
+ if (!module.configure) {
123
+ throw new Error(`Cannot pin "${module.name}": it declares no configure factory.`);
124
+ }
125
+ const instance = module.configure();
126
+ // Async factories would make the pinned instance unavailable until the module system
127
+ // resolves it later, defeating the point of pinning it for immediate synchronous access
128
+ if (instance instanceof Promise) {
129
+ throw new Error(`Cannot pin "${module.name}": its configure factory returns a promise, so it cannot be resolved synchronously.`);
130
+ }
131
+ this.#configurators.set(module.name, instance);
132
+ const pinnedModule = { ...module, configure: () => instance };
133
+ this.#pinnedModules.set(module.name, pinnedModule);
134
+ this.addConfig({ module: pinnedModule });
135
+ }
136
+ /**
137
+ * Returns the configurator pinned for a module by name.
138
+ *
139
+ * @param name - The module's name, as passed to {@link _pin}.
140
+ * @template TConfig - The specific configurator type expected for this module.
141
+ * @returns The configurator pinned under `name`.
142
+ * @throws {Error} If no configurator has been pinned for that name.
143
+ */
144
+ _getConfig(name) {
145
+ const config = this.#configurators.get(name);
146
+ // A missing entry means _pin was never called for this module name
147
+ if (config === undefined) {
148
+ throw new Error(`No configurator is pinned for module "${name}" — call this._pin(module) before this._getConfig("${name}").`);
149
+ }
150
+ return config;
151
+ }
152
+ /**
153
+ * Configures the app's named HTTP clients.
154
+ *
155
+ * @remarks
156
+ * The same {@link IHttpClientConfigurator} the `http` module is configured
157
+ * from — the real one, not a test double. Every client it builds —
158
+ * including ones registered through
159
+ * {@link AppConfigurator.configureHttpClient} or
160
+ * {@link AppConfigurator.useFrameworkServiceClient} — is reachable here to
161
+ * register a short-circuiting {@link HttpMiddleware} through
162
+ * `addMiddleware`, so it answers from that instead of the network.
163
+ *
164
+ * @returns The real HTTP configurator.
165
+ */
166
+ get http() {
167
+ return this._getConfig(http.name);
168
+ }
169
+ /**
170
+ * Configures the user the app's `msal` module signs in.
171
+ *
172
+ * @remarks
173
+ * The same {@link MsalMockConfigurator} the `msal` module is configured from,
174
+ * so a change made here is what the module sees.
175
+ *
176
+ * @returns The MSAL mock configurator.
177
+ */
178
+ get msal() {
179
+ return this._getConfig(msalMockModule.name);
180
+ }
181
+ }
182
+ export default AppMockConfigurator;
183
+ //# sourceMappingURL=AppMockConfigurator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppMockConfigurator.js","sourceRoot":"","sources":["../../../src/mock/AppMockConfigurator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { AppConfig, enableAppModule } from '@equinor/fusion-framework-module-app';
2
+ import { MockAppClient } from '@equinor/fusion-framework-module-app/mock';
3
+ /**
4
+ * Enables the `app` module on a parent framework configurator, wrapping its
5
+ * client in a {@link MockAppClient} so this app's manifest and config resolve
6
+ * locally.
7
+ *
8
+ * @remarks
9
+ * The underlying http client is still resolved the same way `AppConfigurator`
10
+ * resolves it (a pre-configured client, falling back to service discovery), so
11
+ * a caller pointing `serviceDiscovery` at something else — e.g. a real local
12
+ * mock server — is honored for every request `MockAppClient` doesn't answer
13
+ * itself. {@link mockAppModules} uses this to wire its zero-config default
14
+ * parent; call it directly when building a custom parent (via
15
+ * {@link mockFramework}) that also needs this app's own manifest servable.
16
+ *
17
+ * @param configurator - The parent framework's mock configurator, with `app` in its module set.
18
+ * @param env - The application environment whose manifest (and optional config) should be served.
19
+ * @param assetUri - Overrides the base URI a loaded app's script is imported from.
20
+ * @template TEnv - The application environment descriptor.
21
+ *
22
+ * @example Custom service discovery, same manifest resolution
23
+ * ```typescript
24
+ * const fusion = await mockFramework<[AppModule]>((configurator) => {
25
+ * configurator.serviceDiscovery.setBaseUri('http://localhost:9999');
26
+ * enableAppManifestMock(configurator, env);
27
+ * });
28
+ * const modules = await mockAppModules(undefined, env, fusion);
29
+ * ```
30
+ */
31
+ export function enableAppManifestMock(configurator, env, assetUri) {
32
+ enableAppModule(configurator, (builder) => {
33
+ // only override the default asset base when the caller supplies one; an
34
+ // explicit empty string is meaningful (selects a root-relative script path)
35
+ if (assetUri !== undefined) {
36
+ builder.setAssetUri(assetUri);
37
+ }
38
+ builder.setClient(async ({ requireInstance }) => {
39
+ const http = await requireInstance('http');
40
+ const client = http.hasClient('apps')
41
+ ? http.createClient('apps')
42
+ : await (await requireInstance('serviceDiscovery')).createClient('apps');
43
+ // fall back to a trivial config so `App.initialize()` can resolve without a caller-supplied one
44
+ return new MockAppClient(client, env.manifest, env.config ?? new AppConfig({ environment: {} }));
45
+ });
46
+ });
47
+ }
48
+ export default enableAppManifestMock;
49
+ //# sourceMappingURL=enable-app-manifest-mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enable-app-manifest-mock.js","sourceRoot":"","sources":["../../../src/mock/enable-app-manifest-mock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAkB,MAAM,sCAAsC,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAK1E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,qBAAqB,CACnC,YAAoD,EACpD,GAAS,EACT,QAAiB;IAEjB,eAAe,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE;QACxC,wEAAwE;QACxE,4EAA4E;QAC5E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE;YAC9C,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;gBACnC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;gBAC3B,CAAC,CAAC,MAAM,CAAC,MAAM,eAAe,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC3E,gGAAgG;YAChG,OAAO,IAAI,aAAa,CACtB,MAAM,EACN,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,MAAM,IAAI,IAAI,SAAS,CAAoB,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CACpE,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Zero-configuration application module pipelines for tests.
3
+ *
4
+ * @remarks
5
+ * Lets a test initialize an application's real module pipeline — real
6
+ * `event`/`http`/`msal` modules, real configuration pipeline, real lifecycle —
7
+ * while the boundaries that reach outside the process are substituted with the
8
+ * same deterministic fakes {@link https://www.npmjs.com/package/@equinor/fusion-framework | @equinor/fusion-framework}'s
9
+ * own `/mock` entry point uses. No credentials, no network access and no
10
+ * running parent portal are required.
11
+ *
12
+ * This entry point is test-runner agnostic: it contains no dependency on
13
+ * Vitest or any other test framework, so the same helpers work under any
14
+ * runner.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ export { mockAppModules } from './mock-app-modules.js';
19
+ export { AppMockConfigurator } from './AppMockConfigurator.js';
20
+ export { enableAppManifestMock } from './enable-app-manifest-mock.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mock/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAA2B,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { mockFramework } from '@equinor/fusion-framework/mock';
2
+ import { initializeAppModules } from '../initialize-app-modules.js';
3
+ import { AppMockConfigurator } from './AppMockConfigurator.js';
4
+ import { enableAppManifestMock } from './enable-app-manifest-mock.js';
5
+ /**
6
+ * Runs an application's module pipeline with no real credentials required,
7
+ * from either the parent framework or the app's own `http`/`msal`
8
+ * registrations.
9
+ *
10
+ * @remarks
11
+ * The real `AppConfigurator` pipeline is run — the real module set, the real
12
+ * configuration pipeline and the real lifecycle. Only the boundaries that
13
+ * would need credentials are substituted by default; the `http` module is
14
+ * the real `HttpClientConfigurator`, so a registered client only avoids the
15
+ * network for requests a middleware short-circuits with a response — a
16
+ * client with no matching middleware, or a middleware that calls `next`,
17
+ * still reaches the real network. A test exercises the wiring an
18
+ * application actually depends on rather than a reimplementation of it.
19
+ *
20
+ * The configurator passed to `cb` is an {@link AppMockConfigurator}, which *is*
21
+ * an `AppConfigurator`. `useFrameworkServiceClient`, `configureHttpClient` and
22
+ * any callback written for a real app work against it unchanged.
23
+ *
24
+ * `fusion` defaults to a fresh {@link mockFramework} instance with a real `app`
25
+ * module already enabled and this app's own manifest served at whatever URI
26
+ * service discovery resolves `'apps'` to, so a test needs no parent Fusion
27
+ * instance of its own — but an already-mocked (or real) instance can be passed
28
+ * to compose with other framework-level setup. To point the parent's service
29
+ * discovery at something else (e.g. a real local mock server) while keeping
30
+ * the manifest served consistently, build that `fusion` with
31
+ * {@link mockFramework} and call {@link enableAppManifestMock} yourself,
32
+ * after customizing `serviceDiscovery`.
33
+ *
34
+ * @template TModules - Module descriptors beyond the default set. Supply this
35
+ * when a test registers application modules, so they are typed on the result.
36
+ * @template TEnv - The application environment descriptor.
37
+ * @param cb - Configuration callback invoked before module initialization, or `undefined` to skip it.
38
+ * @param env - The application environment (manifest, config, basename).
39
+ * @param fusion - The parent Fusion instance; defaults to a fresh {@link mockFramework} instance.
40
+ * @returns The initialized application module instance.
41
+ *
42
+ * @example Zero configuration
43
+ * ```typescript
44
+ * const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
45
+ * const modules = await mockAppModules(undefined, { manifest });
46
+ * ```
47
+ *
48
+ * @example Register a client answered by the app's own mocked HTTP module
49
+ * ```typescript
50
+ * const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
51
+ * const modules = await mockAppModules(
52
+ * (configurator) => {
53
+ * configurator.useFrameworkServiceClient('portal-api');
54
+ * configurator.http.addMiddleware(async (uri, init, next) =>
55
+ * uri === 'https://portal-api.fusion.test/items' ? Response.json([{ id: 1 }]) : next(uri, init),
56
+ * );
57
+ * },
58
+ * { manifest },
59
+ * );
60
+ *
61
+ * const items = await modules.http.createClient('portal-api').json('/items');
62
+ * ```
63
+ */
64
+ export async function mockAppModules(cb, env, fusion) {
65
+ // `await` is illegal in a parameter default, so an omitted fusion is resolved here instead.
66
+ // The default parent also carries a real `app` module, serving this app's own manifest at
67
+ // whatever URI service discovery is currently configured to resolve `'apps'` to, so a test
68
+ // exercises the same portal wiring a real parent framework would provide.
69
+ const resolvedFusion = fusion ??
70
+ (await mockFramework((configurator) => enableAppManifestMock(configurator, env)));
71
+ const configurator = new AppMockConfigurator(env);
72
+ // Cast is safe: `initializeAppModules` returns the exact module instance produced by
73
+ // `configurator`, which was constructed with this same `TModules`. TypeScript widens
74
+ // `TModules` to its constraint (`AnyModule[]`) when inferring through the generic
75
+ // `TConfigurator` parameter, so the assignment needs an explicit assertion here.
76
+ return initializeAppModules(configurator, cb, {
77
+ fusion: resolvedFusion,
78
+ env,
79
+ });
80
+ }
81
+ export default mockAppModules;
82
+ //# sourceMappingURL=mock-app-modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-app-modules.js","sourceRoot":"","sources":["../../../src/mock/mock-app-modules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAK/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAatE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAIlC,EAAkD,EAClD,GAAS,EACT,MAAe;IAEf,4FAA4F;IAC5F,0FAA0F;IAC1F,2FAA2F;IAC3F,0EAA0E;IAC1E,MAAM,cAAc,GAClB,MAAM;QACN,CAAC,MAAM,aAAa,CAAc,CAAC,YAAY,EAAE,EAAE,CAAC,qBAAqB,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACjG,MAAM,YAAY,GAAG,IAAI,mBAAmB,CAAoC,GAAG,CAAC,CAAC;IACrF,qFAAqF;IACrF,qFAAqF;IACrF,kFAAkF;IAClF,iFAAiF;IACjF,OAAO,oBAAoB,CAAC,YAAY,EAAE,EAAE,EAAE;QAC5C,MAAM,EAAE,cAAc;QACtB,GAAG;KACJ,CAA0C,CAAC;AAC9C,CAAC;AAED,eAAe,cAAc,CAAC"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '13.0.0';
2
+ export const version = '14.0.0-next.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC"}