@digipair/skill-keycloak 0.90.0 → 0.91.0-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/.swcrc ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "jsc": {
3
+ "target": "es2017",
4
+ "parser": {
5
+ "syntax": "typescript",
6
+ "decorators": true,
7
+ "dynamicImport": true
8
+ },
9
+ "transform": {
10
+ "decoratorMetadata": true,
11
+ "legacyDecorator": true
12
+ },
13
+ "keepClassNames": true,
14
+ "externalHelpers": true,
15
+ "loose": true
16
+ },
17
+ "module": {
18
+ "type": "es6"
19
+ },
20
+ "sourceMaps": true,
21
+ "exclude": [
22
+ "jest.config.ts",
23
+ ".*\\.spec.tsx?$",
24
+ ".*\\.test.tsx?$",
25
+ "./src/jest-setup.ts$",
26
+ "./**/jest-setup.ts$"
27
+ ]
28
+ }
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # mylib
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build mylib` to build the library.
@@ -0,0 +1,22 @@
1
+ import baseConfig from '../../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: [
12
+ '{projectRoot}/eslint.config.{js,cjs,mjs}',
13
+ '{projectRoot}/rollup.config.{js,ts,mjs,mts,cjs,cts}',
14
+ ],
15
+ },
16
+ ],
17
+ },
18
+ languageOptions: {
19
+ parser: await import('jsonc-eslint-parser'),
20
+ },
21
+ },
22
+ ];
package/package.json CHANGED
@@ -1,14 +1,28 @@
1
1
  {
2
2
  "name": "@digipair/skill-keycloak",
3
- "version": "0.90.0",
3
+ "version": "0.91.0-0",
4
+ "type": "module",
5
+ "main": "dist/libs/skill-keycloak/index.cjs.js",
6
+ "module": "dist/libs/skill-keycloak/index.esm.js",
7
+ "types": "dist/libs/skill-keycloak/index.esm.d.ts",
8
+ "exports": {
9
+ "./package.json": "./libs/skill-keycloak/package.json",
10
+ ".": {
11
+ "development": "./dist/libs/skill-keycloak/src/index.ts",
12
+ "types": "./dist/libs/skill-keycloak/index.esm.d.ts",
13
+ "import": "./dist/libs/skill-keycloak/index.esm.js",
14
+ "default": "./dist/libs/skill-keycloak/index.cjs.js"
15
+ }
16
+ },
4
17
  "keywords": [
5
18
  "digipair",
6
- "service",
7
- "util"
19
+ "util",
20
+ "service"
8
21
  ],
9
- "dependencies": {
10
- "jsdom": "^25.0.1"
22
+ "nx": {
23
+ "name": "skill-keycloak"
11
24
  },
12
- "main": "./index.cjs.js",
13
- "module": "./index.esm.js"
14
- }
25
+ "dependencies": {
26
+ "@digipair/engine": "0.91.0-0"
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ const { withNx } = require('@nx/rollup/with-nx');
2
+
3
+ module.exports = withNx(
4
+ {
5
+ main: 'libs/skill-keycloak/src/index.ts',
6
+ outputPath: 'dist/libs/skill-keycloak',
7
+ tsConfig: 'libs/skill-keycloak/tsconfig.lib.json',
8
+ compiler: 'swc',
9
+ format: ['esm', "cjs"],
10
+ assets: [
11
+ {
12
+ input: 'libs/skill-keycloak/',
13
+ glob: 'package.json',
14
+ output: '.'
15
+ },
16
+ {
17
+ input: 'libs/skill-keycloak/src/',
18
+ glob: '*.json',
19
+ output: '.'
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ // Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options
25
+ // e.g.
26
+ // output: { sourcemap: true },
27
+ }
28
+ );
@@ -0,0 +1 @@
1
+ declare module 'handlebars/dist/handlebars.min.js';
package/src/jsdom.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module 'jsdom';
@@ -0,0 +1,7 @@
1
+ import { skillKeycloak } from './skill-keycloak';
2
+
3
+ describe('skillKeycloak', () => {
4
+ it('should work', () => {
5
+ expect(skillKeycloak()).toEqual('skill-keycloak');
6
+ });
7
+ });
@@ -0,0 +1,531 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { PinsSettings, executePinsList, generateElementFromPins } from '@digipair/engine';
3
+ import * as jwt from 'jsonwebtoken';
4
+ import { JSDOM } from 'jsdom';
5
+ import { readFile } from 'fs/promises';
6
+ import { lookup } from 'mime-types';
7
+
8
+ class KeycloakService {
9
+ private filteredWebPinsSettings(item: any, path: string): any {
10
+ if (Array.isArray(item)) {
11
+ return item.map((subItem: any, subIndex: number) =>
12
+ this.filteredWebPinsSettings(subItem, `${path}[${subIndex}]`),
13
+ );
14
+ }
15
+
16
+ if (typeof item !== 'object' || item === null) {
17
+ return item;
18
+ }
19
+
20
+ if (item.library === '@digipair/skill-keycloak' && item.element === 'executeFactory') {
21
+ return {
22
+ library: item.library,
23
+ element: item.element,
24
+ properties: {
25
+ path,
26
+ },
27
+ };
28
+ }
29
+
30
+ const result = {} as any;
31
+ Object.entries(item).forEach(([key, value]) => {
32
+ result[key] = this.filteredWebPinsSettings(value, `${path}.${key}`);
33
+ });
34
+
35
+ return result;
36
+ }
37
+
38
+ private findFactoryPinsSettings(path: string, item: PinsSettings[]): PinsSettings {
39
+ const pinsSettings = path.split('.').reduce(
40
+ (acc: any, key: string) => {
41
+ if (key.indexOf('[') !== -1) {
42
+ const index = parseInt((key.match(/\d+/) as any[])[0]);
43
+ return acc[key.split('[')[0]][index];
44
+ }
45
+
46
+ return acc[key];
47
+ },
48
+ { [path.split('[')[0]]: item },
49
+ );
50
+
51
+ return pinsSettings;
52
+ }
53
+
54
+ private async decodedToken(url: string, realm: string, token: string, signal: AbortSignal) {
55
+ const response = await fetch(`${url}/realms/${realm}/protocol/openid-connect/certs`, {
56
+ signal,
57
+ });
58
+ const infos = await response.json();
59
+ const publicKey = `-----BEGIN CERTIFICATE-----\n${
60
+ infos.keys.find(({ alg }: { alg: string; x5c: string }) => alg === 'RS256').x5c[0]
61
+ }\n-----END CERTIFICATE-----`;
62
+ const decodedtoken = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as any;
63
+
64
+ return decodedtoken;
65
+ }
66
+
67
+ private prepareBrowserPinsSettings(name: string, pinsSettings: PinsSettings[]): PinsSettings[] {
68
+ const preparedPinsSettings = pinsSettings.map((item: PinsSettings, index: number) =>
69
+ this.filteredWebPinsSettings(item, `${name}[${index}]`),
70
+ ) as PinsSettings[];
71
+
72
+ return preparedPinsSettings;
73
+ }
74
+
75
+ private mergeConttext(context: any, newContext: any) {
76
+ const output = { ...context };
77
+
78
+ for (const key in newContext) {
79
+ if (Object.prototype.hasOwnProperty.call(newContext, key)) {
80
+ if (
81
+ key !== 'protected' &&
82
+ typeof newContext[key] === 'object' &&
83
+ newContext[key] !== null &&
84
+ !Array.isArray(newContext[key]) &&
85
+ Object.prototype.hasOwnProperty.call(context, key)
86
+ ) {
87
+ output[key] = { ...context[key], ...newContext[key] };
88
+ } else if (typeof newContext[key] !== 'undefined') {
89
+ output[key] = newContext[key];
90
+ }
91
+ }
92
+ }
93
+
94
+ return output;
95
+ }
96
+
97
+ private async pins2html(pins: PinsSettings[], context: any): Promise<string> {
98
+ const dom = new JSDOM();
99
+ const element = dom.window.document.createElement('section');
100
+
101
+ await this.generateElementsFromPins(
102
+ pins,
103
+ element,
104
+ {
105
+ config: {
106
+ VERSIONS: context.config.VERSIONS || {},
107
+ },
108
+ variables: context.variables || {},
109
+ request: context.request || {},
110
+ },
111
+ dom.window.document,
112
+ );
113
+
114
+ return element.innerHTML;
115
+ }
116
+
117
+ private async generateElementsFromPins(
118
+ pinsList: PinsSettings[],
119
+ parent: Element,
120
+ context: any,
121
+ document: Document,
122
+ ): Promise<void> {
123
+ for (let i = 0; i < pinsList.length; i++) {
124
+ const item = pinsList[i];
125
+ await generateElementFromPins(item, parent, context, document, { import: false });
126
+ }
127
+ }
128
+
129
+ private skillKeycloak = `(() => {
130
+ class KeycloakService {
131
+ async authentification() {
132
+ const keycloak = await this._keycloakPromise;
133
+
134
+ if (this.isLogged) {
135
+ await keycloak.updateToken(60000);
136
+ }
137
+
138
+ return keycloak;
139
+ }
140
+
141
+ async initialize(url, realm, clientId) {
142
+ this._keycloakPromise = (async () => {
143
+ const keycloak = new Keycloak({
144
+ url,
145
+ realm,
146
+ clientId,
147
+ });
148
+
149
+ this.isLogged = false;
150
+ try {
151
+ this.isLogged = await keycloak.init({
152
+ onLoad: 'check-sso',
153
+ silentCheckSsoRedirectUri: window.location.origin + '/public/silent-check-sso.html',
154
+ });
155
+ } catch (error) {
156
+ console.error('keycloak error', error);
157
+ }
158
+
159
+ return keycloak;
160
+ })();
161
+
162
+ await this.authentification();
163
+ }
164
+
165
+ async token() {
166
+ const authentification = await this.authentification();
167
+ return authentification.token;
168
+ }
169
+
170
+ async logout(_params, _pinsSettingsList, _context) {
171
+ const authentification = await this.authentification();
172
+ authentification.logout();
173
+ }
174
+
175
+ async login(_params, _pinsSettingsList, _context) {
176
+ const authentification = await this.authentification();
177
+ await authentification.login();
178
+ }
179
+ }
180
+ const keycloakService = new KeycloakService();
181
+ return keycloakService;
182
+ })()`;
183
+
184
+ async page(params: any, _pinsSettingsList: PinsSettings[], context: any): Promise<any> {
185
+ const {
186
+ body,
187
+ head,
188
+ ssr = false,
189
+ styleHtml = '',
190
+ styleBody = '',
191
+ url = context.privates.KEYCLOAK_URL,
192
+ realm = context.privates.KEYCLOAK_REALM,
193
+ clientId = context.privates.KEYCLOAK_CLIENTID,
194
+ factoryInitialize = [],
195
+ browserInitialize = [],
196
+ browserLoad = [],
197
+ confirmBeforeUnload = 'false',
198
+ logged = [],
199
+ unlogged = [],
200
+ factoryUrl = context.privates.FACTORY_URL ||
201
+ process.env['FACTORY_URL'] ||
202
+ 'https://factory.digipair.ai',
203
+ } = params;
204
+ const engineVersion = context.config.VERSIONS['@digipair/engine'] || 'latest';
205
+ const preparedData = {} as { [key: string]: PinsSettings };
206
+
207
+ if (context.request.params[0] === '__digipair_www__') {
208
+ let result: any;
209
+
210
+ try {
211
+ const fileUrl = context.protected.req.path.split('__digipair_www__/')[1];
212
+
213
+ if (!fileUrl) {
214
+ context.protected.res.status(404);
215
+ return { status: 'not found' };
216
+ }
217
+
218
+ const regex = /^(.*?[^@]+)@(.*?[^\/]+)\/(.*?.+)$/;
219
+ const match = fileUrl.match(regex);
220
+
221
+ if (!match) {
222
+ context.protected.res.status(404);
223
+ return { status: 'not found' };
224
+ }
225
+
226
+ const library = match[1];
227
+ if (
228
+ library !== '@digipair/engine' &&
229
+ !context.config.VERSIONS[library] &&
230
+ !context.config.WEB_VERSIONS[library]
231
+ ) {
232
+ context.protected.res.status(404);
233
+ return { status: 'not found' };
234
+ }
235
+
236
+ if (context.config.VERSIONS[library]) {
237
+ const infos = require(`${library}/package.json`);
238
+ if (!(infos.keywords?.indexOf('digipair') >= 0 && infos.keywords?.indexOf('web') >= 0)) {
239
+ context.protected.res.status(404);
240
+ return { status: 'not found' };
241
+ }
242
+ }
243
+
244
+ const path = match[3];
245
+ let filePath = require.resolve(`${library}/${path}`);
246
+
247
+ const mimeType = lookup(filePath) || 'application/octet-stream';
248
+ context.protected.res.setHeader('Content-Type', mimeType);
249
+
250
+ result = await readFile(filePath, 'utf8');
251
+ } catch (error) {
252
+ context.protected.res.status(404);
253
+ result = { status: 'not found' };
254
+ }
255
+
256
+ return result;
257
+ }
258
+
259
+ if (
260
+ context.request.method === 'POST' &&
261
+ context.request.body?.type === 'DIGIPAIR_EXECUTE_FACTORY'
262
+ ) {
263
+ const param = context.request.body.params.path.split('[')[0];
264
+ const pinsSettings = this.findFactoryPinsSettings(
265
+ context.request.body.params.path,
266
+ params[param],
267
+ );
268
+ const pinsSettingsList = pinsSettings.properties?.['execute'] || [];
269
+ const token =
270
+ /^Bearer /g.test(context.request.headers.authorization) &&
271
+ context.request.headers.authorization?.replace(/^Bearer /g, '');
272
+
273
+ if (token) {
274
+ context.keycloak = {
275
+ isLogged: true,
276
+ decodedToken: await this.decodedToken(
277
+ url,
278
+ realm,
279
+ context.request.headers.authorization.replace(/^Bearer /, ''),
280
+ context.protected?.signal,
281
+ ),
282
+ };
283
+ } else {
284
+ context.keycloak = {
285
+ isLogged: false,
286
+ };
287
+ }
288
+
289
+ if (
290
+ (typeof pinsSettings.properties?.['secured'] === 'undefined' ||
291
+ pinsSettings.properties?.['secured']) &&
292
+ !context.keycloak.decodedToken
293
+ ) {
294
+ throw new Error('Unauthorized');
295
+ }
296
+
297
+ return JSON.stringify(
298
+ await executePinsList(
299
+ pinsSettingsList,
300
+ this.mergeConttext(context.request.body.context, context),
301
+ `${context.request.body.params.path}.execute`
302
+ ),
303
+ );
304
+ }
305
+
306
+ const path = context.protected.req.path.replace(/\/$/g, '');
307
+ const baseUrl =
308
+ (context.request.headers['x-forwarded-proto'] ?? context.protected.req.protocol) +
309
+ '://' +
310
+ context.request.headers.host +
311
+ (context.request.params.length <= 0 || context.request.params[0] === ''
312
+ ? path
313
+ : path.substring(0, path.length - context.request.params.join('/').length - 1)) +
314
+ '/__digipair_www__';
315
+
316
+ await executePinsList(factoryInitialize, context, `${context.__PATH__}.factoryInitialize`);
317
+
318
+ const html = `
319
+ <!DOCTYPE html>
320
+ <html style="${styleHtml}">
321
+ <head>
322
+ ${
323
+ head
324
+ ? await this.pins2html(head, context)
325
+ : `
326
+ <meta charset="UTF-8" />
327
+ <title>Digipair</title>
328
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
329
+ <link rel="icon" type="image/x-icon" href="https://res.cloudinary.com/do87nxq3l/image/upload/fl_preserve_transparency/v1717769492/logo-digipair_oyvvxz.png?_s=public-apps">
330
+ `
331
+ }
332
+ </head>
333
+ <body style="${styleBody}">
334
+ <script type="module">
335
+ import '${url}/js/keycloak.js';
336
+ import { config, executePinsList, generateElementFromPins, applyTemplate } from '${baseUrl}/@digipair/engine@${engineVersion}/index.esm.js';
337
+
338
+ const serverUrl = '${factoryUrl}';
339
+ const keycloakService = ${this.skillKeycloak};
340
+
341
+ const originalFetch = window.fetch;
342
+ window.fetch = async (url, options) => {
343
+ if (typeof(url) !== 'string' || !url.includes(serverUrl)) {
344
+ return originalFetch(url, options);
345
+ }
346
+
347
+ return originalFetch(url, {
348
+ ...options,
349
+ headers: {
350
+ ...options.headers,
351
+ 'Content-Type': 'application/json',
352
+ 'Authorization': keycloakService.isLogged ? 'Bearer ' + await keycloakService.token() : undefined,
353
+ },
354
+ });
355
+ };
356
+
357
+ const skillWeb = {
358
+ executeFactory: async (params, pinsSettingsList, context) => {
359
+ const result = await fetch(window.location, {
360
+ headers: {
361
+ 'Content-Type': 'application/json',
362
+ 'Authorization': keycloakService.isLogged ? 'Bearer ' + await keycloakService.token() : undefined,
363
+ },
364
+ body: JSON.stringify({ type: 'DIGIPAIR_EXECUTE_FACTORY', params, pinsSettingsList, context }),
365
+ method: 'POST',
366
+ });
367
+
368
+ return await result.json();
369
+ },
370
+
371
+ logout: (params, pinsSettingsList, context) =>
372
+ keycloakService.logout(),
373
+
374
+ login: (params, pinsSettingsList, context) =>
375
+ keycloakService.login(),
376
+ };
377
+
378
+ config.set('LIBRARIES', { '@digipair/skill-keycloak': skillWeb });
379
+ config.set('BASE_URL', '${baseUrl}');
380
+
381
+ // Keycloak initialization
382
+ await keycloakService.initialize(
383
+ ${JSON.stringify(url)},
384
+ ${JSON.stringify(realm)},
385
+ ${JSON.stringify(clientId)});
386
+
387
+ const context = {
388
+ config: {
389
+ VERSIONS: ${JSON.stringify(context.config.VERSIONS || {})},
390
+ },
391
+ variables: ${JSON.stringify(context.variables || {})},
392
+ request: ${JSON.stringify(context.request || {})},
393
+ keycloak: { isLogged: keycloakService.isLogged },
394
+ __PATH__: '${context.__PATH__}',
395
+ };
396
+
397
+ if (keycloakService.isLogged) {
398
+ await executePinsList(${JSON.stringify(
399
+ this.prepareBrowserPinsSettings('logged', logged),
400
+ )}, context, context.__PATH__ + '.logged');
401
+ } else {
402
+ await executePinsList(${JSON.stringify(
403
+ this.prepareBrowserPinsSettings('unlogged', unlogged),
404
+ )}, context, context.__PATH__ + '.unlogged');
405
+ }
406
+
407
+ // Pins initialization
408
+ await executePinsList(${JSON.stringify(
409
+ this.prepareBrowserPinsSettings('browserInitialize', browserInitialize),
410
+ )}, context, context.__PATH__ + '.browserInitialize');
411
+
412
+ const pinsList = ${JSON.stringify(this.prepareBrowserPinsSettings('body', body))};
413
+ document.querySelectorAll('body > [data-digipair-pins]').forEach((element) => element.remove()); // Remove SSR elements
414
+ for (let i = 0; i < pinsList.length; i++) {
415
+ const item = pinsList[i];
416
+ await generateElementFromPins(item, document.body, { ...context, data: ${JSON.stringify(
417
+ preparedData,
418
+ )} });
419
+ }
420
+
421
+ setTimeout(async () => {
422
+ await executePinsList(${JSON.stringify(
423
+ this.prepareBrowserPinsSettings('browserLoad', browserLoad),
424
+ )}, context, context.__PATH__ + '.browserLoad');
425
+ }, 1);
426
+
427
+ window.addEventListener('beforeunload', (event) => {
428
+ const showConfirmationMessage = applyTemplate('EVALUATE:' + ${JSON.stringify(confirmBeforeUnload)}, context);
429
+
430
+ if (showConfirmationMessage) {
431
+ event.preventDefault();
432
+ event.returnValue = '';
433
+ }
434
+ });
435
+ </script>
436
+
437
+ ${ssr ? await this.pins2html(body, context) : ''}
438
+ </body>
439
+ </html>
440
+ `;
441
+
442
+ return html;
443
+ }
444
+
445
+ async service(params: any, _pinsSettingsList: PinsSettings[], context: any): Promise<any> {
446
+ const {
447
+ execute,
448
+ secured = true,
449
+ url = context.privates.KEYCLOAK_URL,
450
+ realm = context.privates.KEYCLOAK_REALM,
451
+ } = params;
452
+ const token =
453
+ /^Bearer /g.test(context.request.headers.authorization) &&
454
+ context.request.headers.authorization?.replace(/^Bearer /g, '');
455
+
456
+ if (token) {
457
+ context.keycloak = {
458
+ isLogged: true,
459
+ decodedToken: await this.decodedToken(
460
+ url,
461
+ realm,
462
+ context.request.headers.authorization.replace(/^Bearer /, ''),
463
+ context.protected?.signal,
464
+ ),
465
+ };
466
+ } else {
467
+ context.keycloak = {
468
+ isLogged: false,
469
+ };
470
+ }
471
+
472
+ if (secured && !context.keycloak.decodedToken) {
473
+ throw new Error('Unauthorized');
474
+ }
475
+
476
+ return await executePinsList(execute, context, `${context.__PATH__}.execute`);
477
+ }
478
+
479
+ async boost(params: any, _pinsSettingsList: PinsSettings[], context: any) {
480
+ if (context.request.method && context.request.method !== 'POST') {
481
+ return { error: 'Method not allowed' };
482
+ }
483
+
484
+ const {
485
+ steps,
486
+ secured = true,
487
+ url = context.privates.KEYCLOAK_URL,
488
+ realm = context.privates.KEYCLOAK_REALM,
489
+ } = params;
490
+ const token =
491
+ /^Bearer /g.test(context.request.headers.authorization) &&
492
+ context.request.headers.authorization?.replace(/^Bearer /g, '');
493
+
494
+ if (token) {
495
+ context.keycloak = {
496
+ isLogged: true,
497
+ decodedToken: await this.decodedToken(
498
+ url,
499
+ realm,
500
+ context.request.headers.authorization.replace(/^Bearer /, ''),
501
+ context.protected?.signal,
502
+ ),
503
+ };
504
+ } else {
505
+ context.keycloak = {
506
+ isLogged: false,
507
+ };
508
+ }
509
+
510
+ if (secured && !context.keycloak.decodedToken) {
511
+ throw new Error('Unauthorized');
512
+ }
513
+
514
+ const step = context.request.body.step
515
+ ? steps.findIndex(({ name }: any) => name === context.request.body.step)
516
+ : 0;
517
+ const execute = steps[step]?.execute || [];
518
+
519
+ const result = await executePinsList(execute, { ...context, boost: { steps } }, `${context.__PATH__}.execute`);
520
+ return result;
521
+ }
522
+ }
523
+
524
+ export const page = (params: any, pinsSettingsList: PinsSettings[], context: any) =>
525
+ new KeycloakService().page(params, pinsSettingsList, context);
526
+
527
+ export const service = (params: any, pinsSettingsList: PinsSettings[], context: any) =>
528
+ new KeycloakService().service(params, pinsSettingsList, context);
529
+
530
+ export const boost = (params: any, pinsSettingsList: PinsSettings[], context: any) =>
531
+ new KeycloakService().boost(params, pinsSettingsList, context);
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "../engine"
8
+ },
9
+ {
10
+ "path": "./tsconfig.lib.json"
11
+ }
12
+ ]
13
+ }