@astryxdesign/cli 0.1.7-canary.eb8e07b → 0.1.7
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 +7 -97
- package/docs/integration-authoring.md +105 -0
- package/package.json +9 -9
- package/docs/cli-integrations.doc.mjs +0 -150
package/README.md
CHANGED
|
@@ -423,103 +423,13 @@ data: { checks, summary } }`) that AI agents and scripts can parse.
|
|
|
423
423
|
|
|
424
424
|
## Configuration
|
|
425
425
|
|
|
426
|
-
The CLI reads an optional `astryx.config.
|
|
427
|
-
(a sibling of `package.json`). Every field is optional; with no config file the
|
|
428
|
-
CLI runs on defaults.
|
|
426
|
+
The CLI reads from an optional `astryx.config.mjs` in your project root:
|
|
429
427
|
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
428
|
+
```javascript
|
|
429
|
+
export default {
|
|
430
|
+
templates: {
|
|
431
|
+
get: async id => fetchTemplateFromAPI(id),
|
|
432
|
+
},
|
|
435
433
|
issuesUrl: 'https://github.com/your-org/your-repo/issues',
|
|
436
|
-
}
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
`createConfig` is a type-preserving helper: it returns its argument unchanged
|
|
440
|
-
and exists only to give the config file editor autocomplete and type-checking. A
|
|
441
|
-
plain `export default {}` object works identically. It's exported from
|
|
442
|
-
`@astryxdesign/core` (not the CLI) so your config file gets type feedback
|
|
443
|
-
without depending on the CLI; the same helper is re-exported from
|
|
444
|
-
`@astryxdesign/cli/config` for back-compat.
|
|
445
|
-
|
|
446
|
-
| Field | Type | Purpose |
|
|
447
|
-
| ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
448
|
-
| `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)). |
|
|
449
|
-
| `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker. |
|
|
450
|
-
| `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat). |
|
|
451
|
-
| `experimental.xle.components` | `Record<string, XleComponent>` | Register app-local components so layout (XLE) expressions can reference them by name. Unstable. |
|
|
452
|
-
|
|
453
|
-
The config is validated against a strict schema when the CLI loads it, so an
|
|
454
|
-
unknown field is a hard error rather than a silent no-op. `astryx doctor`
|
|
455
|
-
reports whether the config loads cleanly.
|
|
456
|
-
|
|
457
|
-
## Integrations
|
|
458
|
-
|
|
459
|
-
An **integration** is any npm package that contributes its own components,
|
|
460
|
-
templates, and upgrade codemods to Astryx. The CLI surfaces them next to core's,
|
|
461
|
-
through the same commands, so a consumer can `astryx component`,
|
|
462
|
-
`astryx template`, and `astryx upgrade` across core and every integration
|
|
463
|
-
uniformly. Use it to ship a first-party add-on, publish a third-party component
|
|
464
|
-
library, or share an internal design-system package across apps.
|
|
465
|
-
|
|
466
|
-
The system runs on two files, each with a small typed API:
|
|
467
|
-
|
|
468
|
-
| File | Written by | Role |
|
|
469
|
-
| -------------------------------- | ---------- | ----------------------------------------- |
|
|
470
|
-
| `astryx.config.{ts,mjs,js}` | Consumer | Lists which integration packages to load. |
|
|
471
|
-
| `astryx.integration.{ts,mjs,js}` | Author | Declares what a package contributes. |
|
|
472
|
-
|
|
473
|
-
The consumer side is the `integrations` field of [`astryx.config`](#configuration).
|
|
474
|
-
The author side is the integration manifest below.
|
|
475
|
-
|
|
476
|
-
### The integration manifest
|
|
477
|
-
|
|
478
|
-
A package becomes an integration by exporting a manifest from
|
|
479
|
-
`astryx.integration.{ts,mjs,js}` at its root (a sibling of `package.json`). The
|
|
480
|
-
manifest points at where each kind of contribution lives; identity (name,
|
|
481
|
-
version) comes from `package.json`, not the manifest.
|
|
482
|
-
|
|
483
|
-
```typescript
|
|
484
|
-
import {createIntegration} from '@astryxdesign/core/authoring';
|
|
485
|
-
|
|
486
|
-
export default createIntegration({
|
|
487
|
-
components: './components',
|
|
488
|
-
templates: './templates',
|
|
489
|
-
codemods: './codemods',
|
|
490
|
-
issuesUrl: 'https://github.com/acme/widgets/issues',
|
|
491
|
-
});
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
| Field | Type | Purpose |
|
|
495
|
-
| ------------ | -------- | --------------------------------------------------------------------- |
|
|
496
|
-
| `components` | `string` | Directory holding the package's components and their `.doc.*` files. |
|
|
497
|
-
| `templates` | `string` | Directory holding the package's page/block templates. |
|
|
498
|
-
| `codemods` | `string` | Directory holding upgrade codemods run by `astryx upgrade`. |
|
|
499
|
-
| `issuesUrl` | `string` | Where "report an issue" links for this package's contributions point. |
|
|
500
|
-
|
|
501
|
-
Every field is optional; declare only the roots the package ships.
|
|
502
|
-
`createIntegration` is a type-preserving helper (editor autocomplete and
|
|
503
|
-
type-checking); it lives in `@astryxdesign/core/authoring` and is re-exported
|
|
504
|
-
from `@astryxdesign/cli/integration` for back-compat.
|
|
505
|
-
|
|
506
|
-
### How it works
|
|
507
|
-
|
|
508
|
-
Every command loads the consumer's `astryx.config`, resolves each listed
|
|
509
|
-
integration's manifest from `node_modules`, and discovers its contributions.
|
|
510
|
-
Everything is validated against one strict schema at the load boundary, so the
|
|
511
|
-
CLI presents core and integration contributions through a single, uniform
|
|
512
|
-
surface.
|
|
513
|
-
|
|
514
|
-
Discovery is resilient: a broken or misconfigured integration is skipped with a
|
|
515
|
-
one-line warning on stderr instead of crashing the CLI, and it never corrupts a
|
|
516
|
-
`--json` envelope. To inspect problems, run
|
|
517
|
-
`astryx validate-integration <package>` for a detailed report on one package, or
|
|
518
|
-
`astryx doctor` for an overall health check.
|
|
519
|
-
|
|
520
|
-
For the full authoring walkthrough (component doc format, template packaging
|
|
521
|
-
and `exports` requirements, and codemod authoring), see the guide:
|
|
522
|
-
|
|
523
|
-
```bash
|
|
524
|
-
astryx docs cli-integrations
|
|
434
|
+
};
|
|
525
435
|
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Authoring an Astryx Integration
|
|
2
|
+
|
|
3
|
+
> **Status:** working notes. This should eventually move to the public wiki
|
|
4
|
+
> alongside the rest of the integration-authoring guidance; it lives here for
|
|
5
|
+
> now so it ships and is versioned with the CLI.
|
|
6
|
+
|
|
7
|
+
An **Integration** is an npm package that contributes components, templates, and/or
|
|
8
|
+
codemods to a consumer's design-system workflow. Consumers install the 3rd party
|
|
9
|
+
package, add a line to their astryx.config file:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import {createConfig} from '@astryxdesign/cli/config';
|
|
13
|
+
|
|
14
|
+
export default createConfig({
|
|
15
|
+
integrations: ['@acme/astryx-widgets'],
|
|
16
|
+
...
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then the integration's components and templates will be surfaced alongside Astryx
|
|
21
|
+
components in the Astryx CLI.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
astryx component AcmeCarousel --props
|
|
25
|
+
astryx component --list --package @acme/astryx-widgets
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## The Integration File
|
|
29
|
+
|
|
30
|
+
In order to register your package as an Astryx Integration, create an
|
|
31
|
+
`astryx.integration.{ts,mjs,js}` file as a sibling to your `package.json`. This file
|
|
32
|
+
tells Astryx where to find your components, templates, codemods, etc.
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
// astryx.integration.{ts,mjs,js}
|
|
36
|
+
import {createIntegration} from '@astryxdesign/cli/integration';
|
|
37
|
+
|
|
38
|
+
export default createIntegration({
|
|
39
|
+
components: './components',
|
|
40
|
+
templates: './templates',
|
|
41
|
+
codemods: './codemods',
|
|
42
|
+
issuesUrl: 'https://github.com/acme/widgets/issues',
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Components
|
|
47
|
+
|
|
48
|
+
Your components themselves may be exported from your library as you see fit (consumers
|
|
49
|
+
will still import them from your package) but Astryx CLI will look for a .doc.{ts,mjs,js}
|
|
50
|
+
file with the same stem e.g. `AcmeCarousel.tsx` and `AcmeCarousel.doc.ts`.
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
// AcmeCarousel.doc.ts
|
|
54
|
+
import {createComponentDoc} from '@astryxdesign/cli/doc';
|
|
55
|
+
|
|
56
|
+
export default createComponentDoc({
|
|
57
|
+
name: 'AcmeCarousel',
|
|
58
|
+
description: '...',
|
|
59
|
+
...
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Templates
|
|
64
|
+
|
|
65
|
+
Templates are typically not exported from the package directly, but instead accessed
|
|
66
|
+
via the Astryx CLI. Consumers can look through your templates and materialize them
|
|
67
|
+
into their apps.
|
|
68
|
+
|
|
69
|
+
You define a template with the `createPageTemplate` (for full pages) or `createBlockTemplate`
|
|
70
|
+
(for smaller chunks). e.g. `AcmeLandingPage.tsx`, `AcmeLandingPage.template.ts`
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// AcmeLandingPage.template.ts
|
|
74
|
+
import {createPageTemplate} from '@astryxdesign/cli/template';
|
|
75
|
+
|
|
76
|
+
export default createPageTemplate({
|
|
77
|
+
...
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Note that, since the CLI needs access to the template source code, you need to make sure
|
|
82
|
+
that it is included in your published package. This will also allow us to render previews
|
|
83
|
+
of templates in the future by bundling your template into a doc site build.
|
|
84
|
+
|
|
85
|
+
Typically, this is done via the package.json `exports` key.
|
|
86
|
+
|
|
87
|
+
```jsonc
|
|
88
|
+
{
|
|
89
|
+
"exports": {
|
|
90
|
+
// ...
|
|
91
|
+
"./templates/*.tsx": "./templates/*.tsx",
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
In order to verify that it's working, you can test importing the template component like this:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import('@acme/astryx-widgets/templates/AcmeLandingPage.tsx');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Import **with the `.tsx` extension** — an extensionless specifier won't resolve
|
|
103
|
+
under `moduleResolution: bundler`. The extensionful `"./templates/*.tsx"` export
|
|
104
|
+
above is what lets that import type-check without consumers enabling
|
|
105
|
+
`allowImportingTsExtensions`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.7
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"displayName": "CLI",
|
|
5
5
|
"description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@astryxdesign/charts": "
|
|
83
|
-
"@astryxdesign/core": "
|
|
84
|
-
"@astryxdesign/lab": "
|
|
85
|
-
"@astryxdesign/theme-neutral": "
|
|
82
|
+
"@astryxdesign/charts": "*",
|
|
83
|
+
"@astryxdesign/core": "*",
|
|
84
|
+
"@astryxdesign/lab": "*",
|
|
85
|
+
"@astryxdesign/theme-neutral": "*",
|
|
86
86
|
"gpt-tokenizer": "^3.4.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
@@ -100,10 +100,10 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@astryxdesign/charts": "
|
|
104
|
-
"@astryxdesign/core": "
|
|
105
|
-
"@astryxdesign/lab": "
|
|
106
|
-
"@astryxdesign/theme-neutral": "
|
|
103
|
+
"@astryxdesign/charts": "*",
|
|
104
|
+
"@astryxdesign/core": "*",
|
|
105
|
+
"@astryxdesign/lab": "*",
|
|
106
|
+
"@astryxdesign/theme-neutral": "*",
|
|
107
107
|
"gpt-tokenizer": "^3.4.0"
|
|
108
108
|
},
|
|
109
109
|
"scripts": {
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/** @type {import('../../core/src/docs-types').ReferenceDoc} */
|
|
4
|
-
|
|
5
|
-
export const docs = {
|
|
6
|
-
name: 'cli-integrations',
|
|
7
|
-
title: 'CLI Integrations',
|
|
8
|
-
category: 'guide',
|
|
9
|
-
description:
|
|
10
|
-
'Author an npm package that contributes components, templates, and upgrade codemods to Astryx.',
|
|
11
|
-
|
|
12
|
-
sections: [
|
|
13
|
-
{
|
|
14
|
-
title: 'Overview',
|
|
15
|
-
category: 'guide',
|
|
16
|
-
content: [
|
|
17
|
-
{
|
|
18
|
-
type: 'prose',
|
|
19
|
-
text: 'An integration is an npm package that contributes components, templates, and/or upgrade codemods to a consumer\u2019s design-system workflow. Consumers install the package and add it to their `astryx.config`; from then on the integration\u2019s contributions show up alongside core\u2019s in the same CLI commands.',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
type: 'prose',
|
|
23
|
-
text: 'The system runs on two files. The consumer writes `astryx.config.{ts,mjs,js}` at their project root to list which packages to load. The author writes `astryx.integration.{ts,mjs,js}` at the package root to declare what the package contributes. This page is the author\u2019s guide. For the consumer side, run `npx astryx docs getting-started`.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
type: 'prose',
|
|
27
|
-
text: 'On the consumer side, adding your package is one line:',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
type: 'code',
|
|
31
|
-
lang: 'typescript',
|
|
32
|
-
code: "import {createConfig} from '@astryxdesign/core/config';\n\nexport default createConfig({\n integrations: ['@acme/astryx-widgets'],\n});",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
type: 'prose',
|
|
36
|
-
text: 'Your components and templates then appear next to core\u2019s:',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: 'code',
|
|
40
|
-
lang: 'bash',
|
|
41
|
-
code: 'astryx component --list --package @acme/astryx-widgets\nastryx component AcmeCarousel --props',
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
title: 'The Integration File',
|
|
47
|
-
category: 'guide',
|
|
48
|
-
content: [
|
|
49
|
-
{
|
|
50
|
-
type: 'prose',
|
|
51
|
-
text: 'To register your package as an integration, add an `astryx.integration.{ts,mjs,js}` file as a sibling of your `package.json`. It tells the CLI where to find your components, templates, and codemods. Identity (name, version) comes from your `package.json`, not this file.',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
type: 'code',
|
|
55
|
-
lang: 'typescript',
|
|
56
|
-
code: "// astryx.integration.ts\nimport {createIntegration} from '@astryxdesign/core/authoring';\n\nexport default createIntegration({\n components: './components',\n templates: './templates',\n codemods: './codemods',\n issuesUrl: 'https://github.com/acme/widgets/issues',\n});",
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: 'prose',
|
|
60
|
-
text: 'Every field is optional. Declare only the contribution roots your package ships. `createIntegration` is a type-preserving helper for editor autocomplete and type-checking. It lives in `@astryxdesign/core/authoring` and is also re-exported from `@astryxdesign/cli/integration` for back-compat.',
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
title: 'Components',
|
|
66
|
-
category: 'guide',
|
|
67
|
-
content: [
|
|
68
|
-
{
|
|
69
|
-
type: 'prose',
|
|
70
|
-
text: 'Export your components from your library however you like, and consumers still import them from your package. For each component the CLI should document, ship a `.doc.{ts,mjs,js}` file with the same stem, for example `AcmeCarousel.tsx` alongside `AcmeCarousel.doc.ts`.',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
type: 'code',
|
|
74
|
-
lang: 'typescript',
|
|
75
|
-
code: "// AcmeCarousel.doc.ts\nimport {createComponentDoc} from '@astryxdesign/core/authoring';\n\nexport default createComponentDoc({\n name: 'AcmeCarousel',\n description: 'A carousel that cycles through slides.',\n // props, usage, examples, ...\n});",
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
title: 'Templates',
|
|
81
|
-
category: 'guide',
|
|
82
|
-
content: [
|
|
83
|
-
{
|
|
84
|
-
type: 'prose',
|
|
85
|
-
text: 'Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template with `createPageTemplate` (full pages) or `createBlockTemplate` (smaller chunks) in a `.template.{ts,mjs,js}` file next to the source, for example `AcmeLandingPage.tsx` and `AcmeLandingPage.template.ts`.',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
type: 'code',
|
|
89
|
-
lang: 'typescript',
|
|
90
|
-
code: "// AcmeLandingPage.template.ts\nimport {createPageTemplate} from '@astryxdesign/core/authoring';\n\nexport default createPageTemplate({\n // name, description, preview, ...\n});",
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
type: 'prose',
|
|
94
|
-
text: 'The CLI needs the template source at consume time, so make sure it is included in your published package. This is typically done via the `exports` key in `package.json`. It also lets the docsite render template previews in the future.',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
type: 'code',
|
|
98
|
-
lang: 'jsonc',
|
|
99
|
-
code: '{\n "exports": {\n // ...\n "./templates/*.tsx": "./templates/*.tsx"\n }\n}',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
type: 'prose',
|
|
103
|
-
text: 'To verify it resolves, try importing the template component with its `.tsx` extension. An extensionless specifier will not resolve under `moduleResolution: bundler`, and the extensionful export above is what lets this type-check without consumers enabling `allowImportingTsExtensions`.',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
type: 'code',
|
|
107
|
-
lang: 'typescript',
|
|
108
|
-
code: "import('@acme/astryx-widgets/templates/AcmeLandingPage.tsx');",
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
title: 'Codemods',
|
|
114
|
-
category: 'guide',
|
|
115
|
-
content: [
|
|
116
|
-
{
|
|
117
|
-
type: 'prose',
|
|
118
|
-
text: 'Ship codemods so `astryx upgrade` can migrate consumers across breaking changes in your package. Point the integration file\u2019s `codemods` field at your codemods root, and author each one with `createCodemod` (transforms source files) or `createConfigCodemod` (rewrites the consumer\u2019s `astryx.config`).',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
type: 'code',
|
|
122
|
-
lang: 'typescript',
|
|
123
|
-
code: "// codemods/v2-rename-prop.ts\nimport {createCodemod} from '@astryxdesign/cli/codemod';\n\nexport default createCodemod({\n // version, description, transform, ...\n});",
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
type: 'prose',
|
|
127
|
-
text: 'The codemod helpers live in `@astryxdesign/cli/codemod`, not `@astryxdesign/core/authoring` like the doc, integration, and template helpers. Consumers can also run their own post-codemod hooks, such as a reinstall or rebuild, via `hooks.postCodemod` in their `astryx.config`.',
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
title: 'How It Works',
|
|
133
|
-
category: 'guide',
|
|
134
|
-
content: [
|
|
135
|
-
{
|
|
136
|
-
type: 'prose',
|
|
137
|
-
text: 'Every CLI command loads the consumer\u2019s `astryx.config`, resolves each listed integration\u2019s manifest from `node_modules`, and discovers its contributions. Everything is validated against one strict schema at the load boundary. The `create*` helpers do not validate. They are identity functions whose value is their TypeScript surface, so validation happens when the CLI loads the file, not when you author it.',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
type: 'prose',
|
|
141
|
-
text: 'Discovery is resilient. A broken or misconfigured integration is skipped with a single non-blocking warning on stderr instead of crashing the CLI, and it never corrupts a `--json` stdout envelope. Everyday commands keep working with the remaining valid contributions.',
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: 'prose',
|
|
145
|
-
text: 'To inspect problems, run `astryx validate-integration <package>` for a detailed report on one package, or `astryx doctor` for an overall health check of the setup.',
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
};
|