@ni-kismet/sl-webapp-nipkg 0.2.2 → 1.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/README.md CHANGED
@@ -10,6 +10,9 @@ A flexible tool for packaging web applications into SystemLink WebApp `.nipkg` f
10
10
 
11
11
  - 🚀 **Easy Integration**: Works seamlessly with any web application
12
12
  - 📦 **Automated Packaging**: Builds and packages your app in one command
13
+ - 🏪 **App Store Ready**: Embed rich catalog metadata — display name, icon, screenshots, category, license, and more
14
+ - 🚢 **SystemLink Deployment**: Create/update WebApps and upload `.nipkg` content from CLI
15
+ - 🔐 **Secure Authentication**: `login`/`logout` commands store credentials per-host — no secrets in config files
13
16
  - ⚙️ **Flexible Configuration**: Optional config files - use CLI flags or JSON config
14
17
  - 🎯 **TypeScript Support**: Written in TypeScript with full type definitions
15
18
  - 🌈 **Beautiful CLI**: Colorful, informative command-line interface
@@ -20,7 +23,7 @@ A flexible tool for packaging web applications into SystemLink WebApp `.nipkg` f
20
23
 
21
24
  ## Prerequisites
22
25
 
23
- - Node.js 16 or higher (only to run the packaging tool)
26
+ - Node.js 20 or higher (only to run the packaging tool)
24
27
 
25
28
  ## Installation
26
29
 
@@ -71,15 +74,19 @@ sl-webapp-nipkg build \
71
74
 
72
75
  ```json
73
76
  {
77
+ "package": "my-webapp",
74
78
  "maintainer": "John Doe <john.doe@company.com>",
75
79
  "displayName": "My WebApp",
80
+ "section": "WebApps",
81
+ "license": "MIT",
82
+ "appStoreCategory": "Dashboard",
83
+ "appStoreType": "webapp",
76
84
  "buildDir": "dist",
77
- "buildCommand": "npm run build",
78
- "userVisible": true
85
+ "buildCommand": "npm run build"
79
86
  }
80
87
  ```
81
88
 
82
- **Note:** The `name`, `version`, and `description` are auto-detected from your `package.json` if present.
89
+ **Note:** The `package`, `version`, `description`, `homepage`, and `license` are auto-populated from your `package.json` when using `init`. Other fields must be specified explicitly in `nipkg.config.json` if needed.
83
90
 
84
91
  4. **Build and package**:
85
92
 
@@ -161,7 +168,210 @@ sl-webapp-nipkg build \
161
168
 
162
169
  ### `sl-webapp-nipkg init`
163
170
 
164
- Initialize a `nipkg.config.json` file in the current directory.
171
+ Initialize a `nipkg.config.json` file in the current directory. Auto-populates `package`, `maintainer`, `homepage`, and `license` from `package.json`, and sets `section: "WebApps"` and `appStoreType: "webapp"` as defaults.
172
+
173
+ ### `sl-webapp-nipkg generate-manifest`
174
+
175
+ Generate a `manifest.json` for App Store submission from `nipkg.config.json`. Copies all metadata fields while excluding build-only fields (`buildDir`, `buildCommand`, `outputDir`, `buildSuffix`, `deployment`).
176
+
177
+ #### Options
178
+
179
+ - `--config <path>` - Path to nipkg config file (default: `nipkg.config.json`)
180
+ - `--output <path>` - Output path for manifest file (default: `manifest.json`)
181
+ - `--nipkg-file <filename>` - Filename of the `.nipkg` to reference in the manifest
182
+
183
+ #### Example
184
+
185
+ ```bash
186
+ # Generate manifest after build
187
+ sl-webapp-nipkg build
188
+ sl-webapp-nipkg generate-manifest --nipkg-file my-app_1.0.0_all.nipkg
189
+ ```
190
+
191
+ ### `sl-webapp-nipkg login`
192
+
193
+ Authenticate with a SystemLink host and store credentials locally so you don't need to pass `--api-key` on every command.
194
+
195
+ ```bash
196
+ # Interactive — prompts for host and API key
197
+ sl-webapp-nipkg login
198
+
199
+ # Non-interactive — prompts only for API key
200
+ sl-webapp-nipkg login --host https://systemlink.example.com
201
+ ```
202
+
203
+ Credentials are verified against the SystemLink API before being stored. A `401` response means the key was wrong and nothing is saved.
204
+
205
+ - Credentials are stored in `~/.sl-webapp-nipkg/credentials.json` (permissions set to `0600` on Unix)
206
+ - Multiple hosts can be stored side by side
207
+ - The `deploy` and `delete` commands automatically use the stored credential when no explicit key is provided
208
+
209
+ #### Login Options
210
+
211
+ - `--host <url>` - SystemLink host URL (prompted interactively if omitted)
212
+ - `--timeout <ms>` - Verification request timeout in milliseconds (default `30000`)
213
+
214
+ ### `sl-webapp-nipkg logout`
215
+
216
+ Remove the stored credential for a host.
217
+
218
+ ```bash
219
+ # Interactive — prompts for host
220
+ sl-webapp-nipkg logout
221
+
222
+ # Non-interactive
223
+ sl-webapp-nipkg logout --host https://systemlink.example.com
224
+ ```
225
+
226
+ #### Logout Options
227
+
228
+ - `--host <url>` - SystemLink host URL (prompted interactively if omitted)
229
+
230
+ ### `sl-webapp-nipkg deploy`
231
+
232
+ Deploy a `.nipkg` to SystemLink WebApps.
233
+
234
+ Create mode performs two API calls:
235
+
236
+ 1. `POST {host}/niapp/v1/webapps` (creates WebApp and returns `id`)
237
+ 2. `PUT {host}/niapp/v1/webapps/{id}/content` (uploads `.nipkg`)
238
+
239
+ Update mode performs three API calls:
240
+
241
+ 1. `GET {host}/niapp/v1/webapps/{id}` (fetches current data to preserve unchanged fields)
242
+ 2. `PUT {host}/niapp/v1/webapps/{id}` (updates only provided metadata fields)
243
+ 3. `PUT {host}/niapp/v1/webapps/{id}/content` (uploads `.nipkg`)
244
+
245
+ The tool can automatically discover and select `.nipkg` files from your build directory. For updates, you only need to provide the `--app-id` and any fields you want to change; current values are fetched from SystemLink.
246
+
247
+ #### Deploy Options
248
+
249
+ - `--nipkg <path>` - Path to a `.nipkg` file **or** a directory to search for `.nipkg` files (optional - see auto-discovery below)
250
+ - `-b, --build [command]` - Run build command before deploying (optionally specify custom command)
251
+ - `--latest` - Skip interactive selection and auto-pick the newest `.nipkg` (useful for CI/CD)
252
+ - `--build-suffix <suffix>` - Suffix appended to nipkg filename when `--build` is used (e.g., CI build ID)
253
+ - `--skip-cleanup` - Skip cleanup of existing nipkg files when `--build` is used
254
+ - `--host <url>` - SystemLink host URL
255
+ - `--api-key <key>` - API key used as `x-ni-api-key`
256
+ - `--name <name>` - WebApp name (optional for update; falls back to top-level `displayName` from config)
257
+ - `--workspace-id <id>` - Workspace UUID (required for create, optional for update)
258
+ - `--policy-ids <ids>` - Comma-separated policy IDs (optional)
259
+ - `--properties <json>` - JSON properties object (optional, include `version` field if desired)
260
+ - `--update` - Update existing WebApp instead of creating new
261
+ - `--app-id <id>` - Existing WebApp ID (required with `--update`)
262
+ - `--timeout <ms>` - Request timeout in milliseconds (default `30000`)
263
+ - `--config <path>` - Config path (default `nipkg.config.json`)
264
+ - `-v, --verbose` - Verbose output
265
+
266
+ #### Auto-discovery of nipkg Files
267
+
268
+ If `--nipkg` is not provided, the tool will search for `.nipkg` files in:
269
+
270
+ 1. The output directory from the `--build` step (if `--build` was used)
271
+ 2. The `outputDir` from `nipkg.config.json` (defaults to `dist/nipkg`)
272
+
273
+ When `--nipkg` points to a **directory**, the newest `.nipkg` inside it is selected automatically.
274
+
275
+ **Default behavior (interactive selection):**
276
+
277
+ ```bash
278
+ sl-webapp-nipkg deploy --update --app-id <id>
279
+ # If multiple .nipkg files are found, prompts you to pick one
280
+ ```
281
+
282
+ **Auto-select latest (non-interactive, for CI/CD):**
283
+
284
+ ```bash
285
+ sl-webapp-nipkg deploy --update --app-id <id> --latest
286
+ # Always picks the newest .nipkg without prompting
287
+ ```
288
+
289
+ #### Build Before Deploy
290
+
291
+ Build and deploy in a single command:
292
+
293
+ ```bash
294
+ # Build with default build command from config, then deploy
295
+ sl-webapp-nipkg deploy \
296
+ --update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
297
+ --build \
298
+ --latest
299
+
300
+ # Build with custom command, then deploy
301
+ sl-webapp-nipkg deploy \
302
+ --update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
303
+ --build "npm run build:prod" \
304
+ --latest
305
+ ```
306
+
307
+ #### Smart Updates (Fetch Current Data)
308
+
309
+ For update operations, the tool fetches current WebApp data from SystemLink. You only need to provide the fields you want to change:
310
+
311
+ ```bash
312
+ # Update only the properties, keep name/workspace/policyIds unchanged
313
+ sl-webapp-nipkg deploy \
314
+ --update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
315
+ --nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
316
+ --host "$SYSTEMLINK_HOST" \
317
+ --api-key "$SYSTEMLINK_API_KEY" \
318
+ --properties '{"version":"1.0.1","description":"Updated version"}'
319
+
320
+ # Update only the policy IDs, keep other metadata
321
+ sl-webapp-nipkg deploy \
322
+ --update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
323
+ --nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
324
+ --host "$SYSTEMLINK_HOST" \
325
+ --api-key "$SYSTEMLINK_API_KEY" \
326
+ --policy-ids "policy-1,policy-2"
327
+ ```
328
+
329
+ #### Deploy Examples
330
+
331
+ **Create new WebApp:**
332
+
333
+ ```bash
334
+ sl-webapp-nipkg deploy \
335
+ --nipkg ./dist/nipkg/my-app_1.0.0_all.nipkg \
336
+ --host https://test.lifecyclesolutions.ni.com \
337
+ --api-key "$SYSTEMLINK_API_KEY" \
338
+ --name "My WebApp" \
339
+ --workspace-id "45bc27cf-85c6-485c-945d-a51e664f511d" \
340
+ --policy-ids "policy-1,policy-2" \
341
+ --properties '{"description":"my app","version":"1.0.0"}'
342
+ ```
343
+
344
+ **Update existing WebApp (minimal - only app-id required):**
345
+
346
+ ```bash
347
+ sl-webapp-nipkg deploy \
348
+ --nipkg ./dist/nipkg/my-app_1.0.1_all.nipkg \
349
+ --host https://test-api.lifecyclesolutions.ni.com \
350
+ --api-key "$SYSTEMLINK_API_KEY" \
351
+ --update \
352
+ --app-id "35075240-68a1-431b-afe1-9ce76da46de3"
353
+ ```
354
+
355
+ **Build and deploy in single command:**
356
+
357
+ ```bash
358
+ sl-webapp-nipkg deploy \
359
+ --build \
360
+ --update --app-id "35075240-68a1-431b-afe1-9ce76da46de3" \
361
+ --latest \
362
+ --host https://test-api.lifecyclesolutions.ni.com \
363
+ --api-key "$SYSTEMLINK_API_KEY" \
364
+ --properties '{"version":"1.0.1"}'
365
+ ```
366
+
367
+ Environment variable fallback:
368
+
369
+ - `SYSTEMLINK_HOST`
370
+ - `SYSTEMLINK_API_KEY`
371
+
372
+ Precedence: CLI flags > environment variables > stored login credential (`sl-webapp-nipkg login`) > defaults (where applicable).
373
+
374
+ > **Security note:** The API key is intentionally **not** a `nipkg.config.json` field. The recommended workflow is to run `sl-webapp-nipkg login` once per host — credentials are stored in `~/.sl-webapp-nipkg/credentials.json` with restricted permissions. For CI/CD pipelines, use the `SYSTEMLINK_API_KEY` environment variable or the `--api-key` flag.
165
375
 
166
376
  ## Configuration
167
377
 
@@ -171,39 +381,76 @@ Initialize a `nipkg.config.json` file in the current directory.
171
381
 
172
382
  | Property | Type | Required | Description |
173
383
  | -------- | ---- | -------- | ----------- |
174
- | `name` | string | ❌ | Package name (auto-detected from package.json or directory name) |
384
+ | `package` | string | ❌ | Unique package identifier — lowercase with hyphens/dots/underscores (auto-detected from package.json) |
385
+ | `name` | string | ❌ | **Deprecated.** Use `package` instead |
175
386
  | `version` | string | ❌ | Package version (auto-detected from package.json, defaults to 1.0.0) |
176
- | `description` | string | ❌ | Package description (auto-detected from package.json, defaults to empty) |
387
+ | `displayName` | string | ❌ | Human-readable display name shown in the catalog card |
388
+ | `description` | string | ❌ | Short description (auto-detected from package.json) |
389
+ | `section` | string | ❌ | Package section (e.g. `'WebApps'`) |
177
390
  | `maintainer` | string | ❌ | Maintainer information (defaults to 'Unknown') |
178
- | `architecture` | string | ❌ | Target architecture (default: 'all') |
179
- | `displayName` | string | ❌ | Display name for the package |
180
- | `buildDir` | string | ❌* | Build output directory (e.g., 'dist', 'build') - *Required if not provided via CLI |
181
- | `buildCommand` | string | ❌ | Custom build command (default: 'npm run build') |
182
- | `outputDir` | string | ❌ | Custom nipkg output directory (default: 'dist/nipkg') |
391
+ | `homepage` | string | ❌ | Homepage / repository URL (auto-detected from package.json) |
392
+ | `license` | string | ❌ | SPDX license identifier — **required for App Store submissions** (auto-detected from package.json) |
393
+ | `appStoreCategory` | string | | App Store category (e.g. `'Dashboard'`, `'Data Analysis'`) |
394
+ | `appStoreType` | string | ❌ | Resource type: `'webapp'`, `'notebook'`, or `'dashboard'` |
395
+ | `appStoreAuthor` | string | ❌ | Author / company name shown in the catalog |
396
+ | `appStoreTags` | string | ❌ | Comma-separated search tags (e.g. `'assets,calibration'`) |
397
+ | `appStoreRepo` | string | ❌ | Source repository URL |
398
+ | `appStoreMinServerVersion` | string | ❌ | Minimum SystemLink server version (e.g. `'2024 Q4'`) |
399
+ | `iconFile` | string | ❌ | Path to app icon (SVG or PNG, max 128×128 px) — base64-embedded in package |
400
+ | `screenshotFiles` | string[] | ❌ | Paths to up to 3 screenshot files (PNG, max 800×600 px) — base64-embedded in package |
401
+ | `tags` | string | ❌ | Standard nipkg `Tags:` field (falls back to `appStoreTags` if omitted) |
402
+ | `extraControlFields` | object | ❌ | Arbitrary extra control file fields written verbatim as `Key: value` lines |
403
+ | `architecture` | string | ❌ | Target architecture (default: `'windows_all'` for App Store packages) |
404
+ | `userVisible` | boolean \| `'yes'` \| `'no'` | ❌ | Whether the package appears in the App Store catalog (default: `'yes'`) |
405
+ | `buildDir` | string | ❌* | Build output directory (e.g., `'dist'`, `'build'`) — *Required if not provided via CLI |
406
+ | `buildCommand` | string | ❌ | Custom build command (default: `'npm run build'`) |
407
+ | `outputDir` | string | ❌ | Custom nipkg output directory (default: `'dist/nipkg'`) |
183
408
  | `buildSuffix` | string | ❌ | Optional suffix for package filename (e.g., build ID for CI/CD) |
184
409
  | `depends` | string[] | ❌ | Package dependencies |
185
- | `userVisible` | boolean | ❌ | Whether package is user visible |
410
+ | `deployment` | object | ❌ | SystemLink deployment defaults used by `deploy` command |
186
411
 
187
412
  ### Example Configuration
188
413
 
189
414
  ```json
190
415
  {
191
- "name": "my-webapp",
416
+ "package": "my-webapp",
192
417
  "version": "1.2.3",
193
- "description": "A SystemLink WebApp for National Instruments",
418
+ "description": "A SystemLink WebApp for National Instruments",
419
+ "displayName": "My WebApp",
420
+ "section": "WebApps",
194
421
  "maintainer": "John Doe <john.doe@company.com>",
422
+ "homepage": "https://github.com/org/my-webapp",
423
+ "license": "MIT",
424
+ "appStoreCategory": "Dashboard",
425
+ "appStoreType": "webapp",
426
+ "appStoreAuthor": "Acme Corp",
427
+ "appStoreTags": "assets,calibration",
428
+ "appStoreMinServerVersion": "2024 Q4",
429
+ "iconFile": "assets/icon.svg",
195
430
  "architecture": "all",
196
- "displayName": "My WebApp",
431
+ "userVisible": "yes",
197
432
  "buildDir": "dist",
198
433
  "buildCommand": "npm run build",
199
- "userVisible": true,
200
434
  "depends": [
201
435
  "ni-systemlink-server >= 2023.1"
202
436
  ],
203
- "outputDir": "packages"
437
+ "outputDir": "packages",
438
+ "deployment": {
439
+ "host": "https://test.lifecyclesolutions.ni.com",
440
+ "name": "my-webapp",
441
+ "workspaceId": "45bc27cf-85c6-485c-945d-a51e664f511d",
442
+ "policyIds": [],
443
+ "properties": {
444
+ "description": "my webapp",
445
+ "version": "1.2.3"
446
+ },
447
+ "timeout": 30000
448
+ }
204
449
  }
205
450
  ```
206
451
 
452
+ **Note:** The `outputDir` is used by the `deploy` command for automatic `.nipkg` file discovery.
453
+
207
454
  ## Framework Examples
208
455
 
209
456
  ### Non-Node.js Projects
@@ -312,12 +559,16 @@ npm run package:nipkg
312
559
  import { SystemLinkNipkgBuilder, NipkgConfig, BuildOptions } from '@ni-kismet/sl-webapp-nipkg';
313
560
 
314
561
  const config: NipkgConfig = {
315
- name: 'my-app',
562
+ package: 'my-app',
316
563
  version: '1.0.0',
317
564
  description: 'My SystemLink WebApp',
565
+ displayName: 'My App',
566
+ section: 'WebApps',
318
567
  maintainer: 'John Doe <john@example.com>',
568
+ license: 'MIT',
569
+ appStoreCategory: 'Dashboard',
570
+ appStoreType: 'webapp',
319
571
  buildDir: 'dist',
320
- userVisible: true
321
572
  };
322
573
 
323
574
  const options: BuildOptions = {
@@ -359,7 +610,7 @@ jobs:
359
610
  - name: Setup Node.js
360
611
  uses: actions/setup-node@v3
361
612
  with:
362
- node-version: '18'
613
+ node-version: '20'
363
614
  cache: 'npm'
364
615
 
365
616
  - name: Install dependencies
@@ -392,7 +643,7 @@ pool:
392
643
  steps:
393
644
  - task: NodeTool@0
394
645
  inputs:
395
- versionSpec: '18.x'
646
+ versionSpec: '20.x'
396
647
 
397
648
  - script: npm ci
398
649
  displayName: 'Install dependencies'
@@ -432,7 +683,7 @@ your-webapp-project/
432
683
 
433
684
  ## Requirements
434
685
 
435
- - Node.js 16+
686
+ - Node.js 20+
436
687
 
437
688
  ## Development
438
689
 
@@ -460,6 +711,12 @@ sl-webapp-nipkg --help
460
711
 
461
712
  ### Common Issues
462
713
 
714
+ #### "API key is required"
715
+
716
+ - Run `sl-webapp-nipkg login --host <url>` to store credentials for this host, **or**
717
+ - Pass `--api-key "$SYSTEMLINK_API_KEY"` on the command line, **or**
718
+ - Set the `SYSTEMLINK_API_KEY` environment variable
719
+
463
720
  #### "Build directory not found"
464
721
 
465
722
  - Provide build directory via CLI: `--build-dir ./dist`
package/dist/builder.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { NipkgConfig, BuildOptions } from './types.js';
2
+ /**
3
+ * Read a file from disk and return it as a base64-encoded data URI.
4
+ */
5
+ export declare function encodeFileAsBase64(filePath: string): string;
2
6
  export declare class SystemLinkNipkgBuilder {
3
7
  private readonly config;
4
8
  private readonly options;
@@ -8,6 +12,10 @@ export declare class SystemLinkNipkgBuilder {
8
12
  * Build the complete nipkg package
9
13
  */
10
14
  build(): Promise<void>;
15
+ /**
16
+ * Build the complete control file content with all standard and custom fields.
17
+ */
18
+ buildControlFileContent(): string;
11
19
  /**
12
20
  * Get the package name from CLI options, config, or package.json
13
21
  */
@@ -36,5 +44,9 @@ export declare class SystemLinkNipkgBuilder {
36
44
  * Package the application into .nipkg format
37
45
  */
38
46
  private packageApplication;
47
+ /**
48
+ * Resolve userVisible to 'yes' or 'no' string for the control file.
49
+ */
50
+ private resolveUserVisible;
39
51
  }
40
52
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAMvD,qBAAa,sBAAsB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,YAAiB;IAM3D;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BnC;;OAEG;IACH,OAAO,CAAC,OAAO;IA4Bf;;OAEG;IACH,OAAO,CAAC,cAAc;IA0BtB;;OAEG;IACH,OAAO,CAAC,UAAU;IA2BlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB;;OAEG;YACW,sBAAsB;IAwCpC;;OAEG;YACW,kBAAkB;CAkEnC"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA0FvD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa3D;AAoBD,qBAAa,sBAAsB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,YAAiB;IAM3D;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BnC;;OAEG;IACI,uBAAuB,IAAI,MAAM;IA2ExC;;OAEG;IACH,OAAO,CAAC,OAAO;IAgCf;;OAEG;IACH,OAAO,CAAC,cAAc;IA0BtB;;OAEG;IACH,OAAO,CAAC,UAAU;IA2BlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB;;OAEG;YACW,sBAAsB;IAwCpC;;OAEG;YACW,kBAAkB;IA0EhC;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAU7B"}