├── .circleci
└── config.yml
├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── stale.yml
├── .gitignore
├── .node-version
├── .nvmrc
├── .prettierrc
├── .releaserc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── eslint.config.mjs
├── integration-test.mjs
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── cli.ts
├── components
│ ├── client.ts
│ ├── docs.ts
│ ├── index.ts
│ ├── server.ts
│ └── types.ts
├── config.ts
├── custom-test-component.js
├── index.test.ts
└── index.ts
├── templates
├── client
│ ├── rust
│ │ ├── Cargo.toml
│ │ ├── bin
│ │ │ └── test.sh
│ │ └── src
│ │ │ ├── lib.rs
│ │ │ └── test_harness.rs
│ └── typescript
│ │ ├── _package.json
│ │ ├── eslint.config.mjs
│ │ ├── gitignore
│ │ ├── src
│ │ └── index.ts
│ │ └── tsconfig.json
├── docs
│ └── gatsby
│ │ ├── .node-version
│ │ ├── .nvmrc
│ │ ├── .prettierrc
│ │ ├── BUILDING.md
│ │ ├── CONVENTIONAL_COMMITS.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── VERSIONING.md
│ │ ├── _package-lock.json
│ │ ├── _package.json
│ │ ├── eslint.config.mjs
│ │ ├── gatsby-browser.js
│ │ ├── gatsby-node.js
│ │ ├── gatsby-ssr.js
│ │ ├── src
│ │ ├── components
│ │ │ ├── PlaygroundSplitPane.css
│ │ │ ├── PlaygroundSplitPane.tsx
│ │ │ ├── image.tsx
│ │ │ └── seo.tsx
│ │ ├── docs-react-plugins
│ │ │ └── index.tsx
│ │ ├── docs
│ │ │ └── empty.md
│ │ ├── images
│ │ │ ├── gatsby-astronaut.png
│ │ │ └── gatsby-icon.png
│ │ ├── pages
│ │ │ ├── 404.tsx
│ │ │ ├── api-documentation.css
│ │ │ ├── api-documentation.tsx
│ │ │ └── index.tsx
│ │ ├── stores
│ │ │ └── inspectorActionStore.ts
│ │ └── worker.d.ts
│ │ └── tsconfig.json
└── server
│ └── typescript
│ ├── Dockerfile
│ ├── README.md
│ ├── _package.json
│ ├── eslint.config.mjs
│ ├── gitignore
│ ├── jest.config.js
│ ├── src
│ ├── generated-method-mapping.ts
│ ├── index.ts
│ ├── openrpc.json
│ └── server.ts
│ └── tsconfig.json
├── test-generator-config.json
└── tsconfig.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | aliases:
4 | # -------------------------
5 | # ALIASES: Caches
6 | # -------------------------
7 | - &restore-deps-cache
8 | key: deps-cache-{{ checksum "package.json" }}
9 |
10 | - &save-deps-cache
11 | key: deps-cache-{{ checksum "package.json" }}
12 | paths:
13 | - ~/generator/node_modules
14 |
15 | # -------------------------
16 | # ALIASES: Branch Filters
17 | # -------------------------
18 | - &filter-only-master
19 | branches:
20 | only: master
21 | - &filter-only-semantic-pr
22 | branches:
23 | only: /^(pull|dependabot|fix|feat)\/.*$/
24 | - &filter-only-fork-pr
25 | branches:
26 | only: /^pull\/.*$/
27 |
28 | defaults: &defaults
29 | working_directory: ~/generator
30 | docker:
31 | - image: cimg/node:20.12.1
32 | jobs:
33 | test:
34 | <<: *defaults
35 | steps:
36 | - checkout
37 | - restore_cache: *restore-deps-cache
38 | - run: npm install
39 | - run: npm install codecov
40 | - run: npm test
41 | - run: ./node_modules/.bin/codecov
42 | - save_cache: *save-deps-cache
43 |
44 | build:
45 | <<: *defaults
46 | steps:
47 | - checkout
48 | - restore_cache: *restore-deps-cache
49 | - run: npm install
50 | - run: npm run build
51 | - save_cache: *save-deps-cache
52 |
53 | release:
54 | <<: *defaults
55 | steps:
56 | - checkout
57 | - restore_cache: *restore-deps-cache
58 | - run: npm install
59 | - run: npm run build
60 | - run: npm install semantic-release @semantic-release/changelog @semantic-release/git @semantic-release/github @semantic-release/npm @semantic-release/commit-analyzer @semantic-release/release-notes-generator
61 | - run: git checkout package.json package-lock.json
62 | - run: ./node_modules/.bin/semantic-release
63 | - save_cache: *save-deps-cache
64 |
65 | workflows:
66 | version: 2
67 | analysis:
68 | jobs:
69 | - test:
70 | filters: *filter-only-semantic-pr
71 | - build:
72 | filters: *filter-only-semantic-pr
73 | requires:
74 | - test
75 |
76 | release:
77 | jobs:
78 | - test:
79 | filters: *filter-only-master
80 | - build:
81 | filters: *filter-only-master
82 | - hold:
83 | filters: *filter-only-master
84 | type: approval
85 | requires:
86 | - test
87 | - build
88 | - release:
89 | filters: *filter-only-master
90 | context: open-rpc-deployer
91 | requires:
92 | - hold
93 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | indent_style = space
9 | indent_size = 2
10 | tab_width = 2
11 | end_of_line = lf
12 | charset = utf-8
13 | trim_trailing_whitespace = true
14 | insert_final_newline = true
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: stale
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/build
3 | **/coverage
4 | **/.idea
5 | coverage
6 | .DS_Store
7 | test
8 | generated
9 | artifacts
10 | LLMLOG.MD
11 | openrpc.json
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 20.11.1
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.11.1
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "bracketSameLine": false,
4 | "arrowParens": "always",
5 | "bracketSpacing": true,
6 | "useTabs": false,
7 | "tabWidth": 2,
8 | "printWidth": 100,
9 | "trailingComma": "es5",
10 | "singleQuote": true,
11 | "semi": true
12 | }
13 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "tagFormat": "${version}",
3 | "branch": "master",
4 | "plugins": [
5 | "@semantic-release/commit-analyzer",
6 | "@semantic-release/release-notes-generator",
7 | "@semantic-release/changelog",
8 | "@semantic-release/github",
9 | "@semantic-release/git",
10 | "@semantic-release/npm"
11 | ],
12 | "verifyConditions": [
13 | "@semantic-release/changelog",
14 | "@semantic-release/npm",
15 | "@semantic-release/git",
16 | "@semantic-release/github"
17 | ],
18 | "publish": [
19 | "@semantic-release/github",
20 | "@semantic-release/npm"
21 | ],
22 | "success": [
23 | "@semantic-release/github"
24 | ],
25 | "fail": [
26 | "@semantic-release/github"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [2.0.1](https://github.com/open-rpc/generator/compare/2.0.0...2.0.1) (2025-06-05)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * documentation rendering bug with gatsby theme ([e9fd09b](https://github.com/open-rpc/generator/commit/e9fd09b37cd7d7e68bfae8d6fc9f538cba8f4631))
7 |
8 | # [2.0.0](https://github.com/open-rpc/generator/compare/1.22.3...2.0.0) (2025-03-04)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * 🐛 support gatsby pathPrefix, thus fallback relative ([7ae3278](https://github.com/open-rpc/generator/commit/7ae32788d2d9df3ae7d8cfce9b363e186c6a869c))
14 | * add updated eslinting and lint ([9b80ceb](https://github.com/open-rpc/generator/commit/9b80ceb5680686934fb540b90e5130327d10552e))
15 | * bump ([86c4574](https://github.com/open-rpc/generator/commit/86c4574e98a09d9811383e5bb58b64b95b5ee9c1))
16 | * bump circle ci version ([4f5a65a](https://github.com/open-rpc/generator/commit/4f5a65a12bbf0d6294ca0b602558ea08f5b888b5))
17 | * dont break generated client interface ([46034ca](https://github.com/open-rpc/generator/commit/46034cafe2789470c271a39734a4a1addea2026b))
18 | * tests to align with new esm format ([6548914](https://github.com/open-rpc/generator/commit/6548914fb09d110934cd4785be558cc824d122a9))
19 | * this is how we dooooo ([38d41b1](https://github.com/open-rpc/generator/commit/38d41b19919142e842cbed3321088b542987a4bc))
20 | * update template for gatsby config ([81c6452](https://github.com/open-rpc/generator/commit/81c64525ef179b70b62def9f4a4feb7590d44105))
21 |
22 |
23 | ### Features
24 |
25 | * this is a major revision to most packages. The changes support ([7970eaf](https://github.com/open-rpc/generator/commit/7970eafbef683b6432374e45e0bd471401602aa9))
26 |
27 |
28 | ### BREAKING CHANGES
29 |
30 | * most apis will be ESM except for server, and use
31 | the latest versions of packages. This will change some of the
32 | logic or processes you may have been using there is also a bump
33 | to schema-utiils for most packages and meta-schema.
34 |
35 | ## [1.22.3](https://github.com/open-rpc/generator/compare/1.22.2...1.22.3) (2023-01-18)
36 |
37 |
38 | ### Bug Fixes
39 |
40 | * bump @open-rpc/typings which will handle leading numbers in titles ([7229170](https://github.com/open-rpc/generator/commit/722917001db071c0ff9dc93b65717cfd0d848cad))
41 | * update deps that were causing tests to fail ([7e55cfc](https://github.com/open-rpc/generator/commit/7e55cfc5ca3b516e7133ec1cdee69ec9b96fc282))
42 | * update node version and add circle ci context ([acd2d73](https://github.com/open-rpc/generator/commit/acd2d73b4bc44e54b34a66ef59148296fc76c6b0))
43 |
44 | ## [1.22.2](https://github.com/open-rpc/generator/compare/1.22.1...1.22.2) (2021-10-02)
45 |
46 |
47 | ### Bug Fixes
48 |
49 | * add package lock file and lock semi ver on a few packages ([1a0d8d7](https://github.com/open-rpc/generator/commit/1a0d8d75e2544eaae43054e4125c7c8a64d3099b))
50 |
51 | ## [1.22.1](https://github.com/open-rpc/generator/compare/1.22.0...1.22.1) (2021-09-05)
52 |
53 |
54 | ### Bug Fixes
55 |
56 | * server component package scripts merge ([3c21c77](https://github.com/open-rpc/generator/commit/3c21c77e3b66f8e8e5db475b4149aa34d05f2eae))
57 |
58 | # [1.22.0](https://github.com/open-rpc/generator/compare/1.21.2...1.22.0) (2021-08-27)
59 |
60 |
61 | ### Bug Fixes
62 |
63 | * update component in README ([1f708f5](https://github.com/open-rpc/generator/commit/1f708f51770b134cd7e1270150663d8423cadedf))
64 |
65 |
66 | ### Features
67 |
68 | * refactor code to support an override output directory ([6e7937e](https://github.com/open-rpc/generator/commit/6e7937e464b52994feb12f98a95cbd8c78e8e2d3)), closes [#655](https://github.com/open-rpc/generator/issues/655)
69 |
70 | ## [1.21.2](https://github.com/open-rpc/generator/compare/1.21.1...1.21.2) (2021-08-27)
71 |
72 |
73 | ### Bug Fixes
74 |
75 | * custom component relative urls ([b28b205](https://github.com/open-rpc/generator/commit/b28b2059935f0232fc31744f31278714194996f9))
76 | * customComponent test paths ([8389a87](https://github.com/open-rpc/generator/commit/8389a87a741957c1565394a0222526c9fcb26a8c))
77 |
78 | ## [1.21.1](https://github.com/open-rpc/generator/compare/1.21.0...1.21.1) (2021-08-20)
79 |
80 |
81 | ### Bug Fixes
82 |
83 | * bump versions of client and lock typings version to latest ([a1645e9](https://github.com/open-rpc/generator/commit/a1645e9df06424ed0d87d1d7489610e06c252238))
84 |
85 | # [1.21.0](https://github.com/open-rpc/generator/compare/1.20.1...1.21.0) (2021-08-16)
86 |
87 |
88 | ### Features
89 |
90 | * bump typings and lock version ([c356e65](https://github.com/open-rpc/generator/commit/c356e6530034e98a4845e350a97535cc99c617ca))
91 |
92 | ## [1.20.1](https://github.com/open-rpc/generator/compare/1.20.0...1.20.1) (2021-07-17)
93 |
94 |
95 | ### Bug Fixes
96 |
97 | * lodash zipObject wrong order ([5c1a1ef](https://github.com/open-rpc/generator/commit/5c1a1ef2ce651485c8d092cf11b0efab6d8de80a)), closes [#670](https://github.com/open-rpc/generator/issues/670)
98 |
99 | # [1.20.0](https://github.com/open-rpc/generator/compare/1.19.1...1.20.0) (2021-06-05)
100 |
101 |
102 | ### Features
103 |
104 | * add optional field for custom configs to write openrpc doc to disk ([79f9249](https://github.com/open-rpc/generator/commit/79f92498523774272013e51fd938d71044c4bd57))
105 |
106 | ## [1.19.1](https://github.com/open-rpc/generator/compare/1.19.0...1.19.1) (2021-06-04)
107 |
108 |
109 | ### Bug Fixes
110 |
111 | * lock metaschema version for default client ([fbffc78](https://github.com/open-rpc/generator/commit/fbffc78a59c092174622bafe9641308e4bdd6794))
112 | * type on static path interface can be undefined ([c805051](https://github.com/open-rpc/generator/commit/c8050518c338058079097a3273d289845c70cb64))
113 |
114 | # [1.19.0](https://github.com/open-rpc/generator/compare/1.18.13...1.19.0) (2021-06-03)
115 |
116 |
117 | ### Features
118 |
119 | * add support for custom generators ([b1b99de](https://github.com/open-rpc/generator/commit/b1b99de8267f53177e913fd4e799620fb99b9893))
120 |
121 | ## [1.18.13](https://github.com/open-rpc/generator/compare/1.18.12...1.18.13) (2021-06-01)
122 |
123 |
124 | ### Bug Fixes
125 |
126 | * package version updates for client and server ([d248e4a](https://github.com/open-rpc/generator/commit/d248e4aee4ce59860560239274a390d620b6bb19))
127 |
128 | ## [1.18.12](https://github.com/open-rpc/generator/compare/1.18.11...1.18.12) (2021-04-16)
129 |
130 |
131 | ### Bug Fixes
132 |
133 | * force types to be methodobjects ([0fe3cd9](https://github.com/open-rpc/generator/commit/0fe3cd9427dc4cf604da9b3ad0a9c0583ea2209a))
134 | * remove extra deps ([ecf6262](https://github.com/open-rpc/generator/commit/ecf62628604cfbea04fdb5fb4b12869a9375d05e))
135 | * typedoc generation fixed ([77f6d24](https://github.com/open-rpc/generator/commit/77f6d24af1bd90eaecf8d1114040664ad4693a3d))
136 | * update inquirer ([e769c12](https://github.com/open-rpc/generator/commit/e769c12dfc23e8725d45acf8e293f199f6ac9f1f))
137 | * update lodash and fs-extra ([cbcab8b](https://github.com/open-rpc/generator/commit/cbcab8b0738e187cdfb204efd7bbeb47c7c445d8))
138 | * update meta schema nd jest dev deps ([c52c18a](https://github.com/open-rpc/generator/commit/c52c18a1cf69ea9c81b28e3874dccdc82b5bb8a1))
139 | * update open-rpc/typings ([c6584ba](https://github.com/open-rpc/generator/commit/c6584ba4f64ed35d1f3e3ec0ad1b546670111143))
140 | * update to lts nodejs and regenerate packagelock ([08a79d7](https://github.com/open-rpc/generator/commit/08a79d766b3785ad3e345eeb637923183690fa48))
141 | * **gatsby docs:** bump docs-react to get syntax-highlighting ([4baf3d3](https://github.com/open-rpc/generator/commit/4baf3d3a9437178ed2285ab0cb83d2ca9db419ca))
142 |
143 | ## [1.18.11](https://github.com/open-rpc/generator/compare/1.18.10...1.18.11) (2020-12-30)
144 |
145 |
146 | ### Bug Fixes
147 |
148 | * update docs-react for better paramStructure by-name support ([264bbd9](https://github.com/open-rpc/generator/commit/264bbd9d210a42ae23ec764d4c62c26830121efc))
149 |
150 | ## [1.18.10](https://github.com/open-rpc/generator/compare/1.18.9...1.18.10) (2020-12-17)
151 |
152 |
153 | ### Bug Fixes
154 |
155 | * update clientjs ([be2eaf9](https://github.com/open-rpc/generator/commit/be2eaf9db4e3c2a65e858fc9de0b0ebdb63cb660))
156 |
157 | ## [1.18.9](https://github.com/open-rpc/generator/compare/1.18.8...1.18.9) (2020-11-14)
158 |
159 |
160 | ### Bug Fixes
161 |
162 | * **docs:** bump inspector version ([cf4c776](https://github.com/open-rpc/generator/commit/cf4c77640bdd9733880f7ae36ef019adcf2cb825))
163 |
164 | ## [1.18.8](https://github.com/open-rpc/generator/compare/1.18.7...1.18.8) (2020-10-19)
165 |
166 |
167 | ### Bug Fixes
168 |
169 | * lock material-ui versions ([4fca162](https://github.com/open-rpc/generator/commit/4fca162f36adb1051f170b131291ef819d2ce340))
170 |
171 | ## [1.18.7](https://github.com/open-rpc/generator/compare/1.18.6...1.18.7) (2020-10-16)
172 |
173 |
174 | ### Bug Fixes
175 |
176 | * **docs:** update to latest inspector ([9253b96](https://github.com/open-rpc/generator/commit/9253b9679d8fe5671dd9bb2f31abc35037b702b2))
177 |
178 | ## [1.18.6](https://github.com/open-rpc/generator/compare/1.18.5...1.18.6) (2020-09-25)
179 |
180 |
181 | ### Bug Fixes
182 |
183 | * bump client js version ([e32a5c5](https://github.com/open-rpc/generator/commit/e32a5c506d67e7dd76b0de5f7511b6f03c54a0a2))
184 |
185 | ## [1.18.5](https://github.com/open-rpc/generator/compare/1.18.4...1.18.5) (2020-09-02)
186 |
187 |
188 | ### Bug Fixes
189 |
190 | * **deps:** lock @types/fs-extra ([2aff98e](https://github.com/open-rpc/generator/commit/2aff98eb28b9ef9825ad57624671958a76e965df))
191 | * **gatsby docs:** lock @open-rpc/inspector version ([181476f](https://github.com/open-rpc/generator/commit/181476f69b277eec49ba15e4630fbf20928d3f1a))
192 |
193 | ## [1.18.4](https://github.com/open-rpc/generator/compare/1.18.3...1.18.4) (2020-08-05)
194 |
195 |
196 | ### Bug Fixes
197 |
198 | * bump bump ([5d169a3](https://github.com/open-rpc/generator/commit/5d169a36c7e1d761f1eb5001ee98322c1b4e6ba7))
199 | * get tests passing again ([a34b635](https://github.com/open-rpc/generator/commit/a34b635505655c378ce1f4e7d4c6c25edf8043bf))
200 | * more deps ([8017b3f](https://github.com/open-rpc/generator/commit/8017b3f3f6aff37b21d4a2e82e42403a9c37308e))
201 | * rebase ([f9e9d45](https://github.com/open-rpc/generator/commit/f9e9d45b5e9c1d38625af7b6182c37684315180c))
202 | * update deps ([50f25da](https://github.com/open-rpc/generator/commit/50f25da246a9372261fdbaab1a33f5607493c627))
203 | * update more deps ([993d16c](https://github.com/open-rpc/generator/commit/993d16cdc0a7ba32a74a85062a07a0b1a545e9dd))
204 | * update more deps ([473b830](https://github.com/open-rpc/generator/commit/473b830782e2f4b0668432105feec6bb970f21c0))
205 | * upgrade deps ([8be5abb](https://github.com/open-rpc/generator/commit/8be5abb8834a1eaa64f8a8bd265dde47cff55e3f))
206 |
207 | ## [1.18.3](https://github.com/open-rpc/generator/compare/1.18.2...1.18.3) (2020-07-27)
208 |
209 |
210 | ### Bug Fixes
211 |
212 | * update client-js interface ([bed242a](https://github.com/open-rpc/generator/commit/bed242a07755ae8b8e546fafed542caaffcc8156))
213 | * **gatsby docs:** add x-transport on servers to default inspector transport ([128a24d](https://github.com/open-rpc/generator/commit/128a24dddda1dd2f30aa6f2b706502ab3eae0a36))
214 |
215 | ## [1.18.2](https://github.com/open-rpc/generator/compare/1.18.1...1.18.2) (2020-07-21)
216 |
217 |
218 | ### Bug Fixes
219 |
220 | * **docs:** default to first server if exists for inspector ([b6b1fe9](https://github.com/open-rpc/generator/commit/b6b1fe9c92b6b9f0e7251fcf758f2cfd3dc2fce7))
221 | * docs build issue with monaco ([5d1fe22](https://github.com/open-rpc/generator/commit/5d1fe22c6d571b02a743ab358bfab4774370e1a1))
222 |
223 | ## [1.18.1](https://github.com/open-rpc/generator/compare/1.18.0...1.18.1) (2020-07-15)
224 |
225 |
226 | ### Bug Fixes
227 |
228 | * **README:** add documentation to list of features ([89a8c10](https://github.com/open-rpc/generator/commit/89a8c10777269be584bf7289323d45023141822c))
229 | * add typedoc configs to clean ui ([c98a93a](https://github.com/open-rpc/generator/commit/c98a93aa39d5d4d36464805b235d846b730587c8))
230 |
231 | # [1.18.0](https://github.com/open-rpc/generator/compare/1.17.0...1.18.0) (2020-07-13)
232 |
233 |
234 | ### Bug Fixes
235 |
236 | * add docs test ([259e407](https://github.com/open-rpc/generator/commit/259e4079b9625caca973d123442adc5852a18ef8))
237 | * add templates docs dir ([991979b](https://github.com/open-rpc/generator/commit/991979bd848df444ba84bba79318fe5229810f7f))
238 | * remove changelog ([c74fd8d](https://github.com/open-rpc/generator/commit/c74fd8d690632bcca097e7723a9e605d53b44b6e))
239 |
240 |
241 | ### Features
242 |
243 | * add docs generator ([4ef3c92](https://github.com/open-rpc/generator/commit/4ef3c92ef7d3532b545b16390bb62a6696fba951))
244 |
245 | # [1.17.0](https://github.com/open-rpc/generator/compare/1.16.0...1.17.0) (2020-06-25)
246 |
247 |
248 | ### Features
249 |
250 | * update wording ([b655f6b](https://github.com/open-rpc/generator/commit/b655f6b166894cb79d89f05829d031cbdf3cc18e))
251 |
252 | # [1.16.0](https://github.com/open-rpc/generator/compare/1.15.5...1.16.0) (2020-06-25)
253 |
254 |
255 | ### Bug Fixes
256 |
257 | * remove chat on discord badge & languages ([8ee7870](https://github.com/open-rpc/generator/commit/8ee78703ebea47abeeaf1d09d607a0081006711f))
258 |
259 |
260 | ### Features
261 |
262 | * slight improvements to readme ([8c1a55d](https://github.com/open-rpc/generator/commit/8c1a55d95a26afc00b7a6afb12248472fa9fe4d0))
263 | * **typescript:** add postmessage iframe and window support ([9b0eca2](https://github.com/open-rpc/generator/commit/9b0eca2499cb8a9951615b72b3fc31072afbf570))
264 | * add typescript client postmessage support ([e015c34](https://github.com/open-rpc/generator/commit/e015c343e303ca76854bdb44223ca5acd2d650b9))
265 |
266 | # [1.16.0](https://github.com/open-rpc/generator/compare/1.15.5...1.16.0) (2020-06-25)
267 |
268 |
269 | ### Features
270 |
271 | * slight improvements to readme ([8c1a55d](https://github.com/open-rpc/generator/commit/8c1a55d95a26afc00b7a6afb12248472fa9fe4d0))
272 | * **typescript:** add postmessage iframe and window support ([9b0eca2](https://github.com/open-rpc/generator/commit/9b0eca2499cb8a9951615b72b3fc31072afbf570))
273 | * add typescript client postmessage support ([e015c34](https://github.com/open-rpc/generator/commit/e015c343e303ca76854bdb44223ca5acd2d650b9))
274 |
275 | # [1.16.0](https://github.com/open-rpc/generator/compare/1.15.5...1.16.0) (2020-06-25)
276 |
277 |
278 | ### Features
279 |
280 | * **typescript:** add postmessage iframe and window support ([9b0eca2](https://github.com/open-rpc/generator/commit/9b0eca2499cb8a9951615b72b3fc31072afbf570))
281 | * add typescript client postmessage support ([e015c34](https://github.com/open-rpc/generator/commit/e015c343e303ca76854bdb44223ca5acd2d650b9))
282 |
283 | ## [1.15.5](https://github.com/open-rpc/generator/compare/1.15.4...1.15.5) (2020-05-01)
284 |
285 |
286 | ### Bug Fixes
287 |
288 | * remove i prefix on server types ([cabbb29](https://github.com/open-rpc/generator/commit/cabbb297b7c3fb1e61d98172b9c885c1562930e3)), closes [#414](https://github.com/open-rpc/generator/issues/414)
289 |
290 | ## [1.15.4](https://github.com/open-rpc/generator/compare/1.15.3...1.15.4) (2020-03-23)
291 |
292 |
293 | ### Bug Fixes
294 |
295 | * **README:** path to config file ([afaa399](https://github.com/open-rpc/generator/commit/afaa399cf4592e4382b91cb4732280b69a1cecd8))
296 |
297 | ## [1.15.3](https://github.com/open-rpc/generator/compare/1.15.2...1.15.3) (2020-03-04)
298 |
299 |
300 | ### Bug Fixes
301 |
302 | * move type packages to non dev deps ([18d2c89](https://github.com/open-rpc/generator/commit/18d2c89c35e727a035458ed5eb99b15a649ef5ed)), closes [#363](https://github.com/open-rpc/generator/issues/363)
303 |
304 | ## [1.15.2](https://github.com/open-rpc/generator/compare/1.15.1...1.15.2) (2020-02-10)
305 |
306 |
307 | ### Bug Fixes
308 |
309 | * update package-lock ([7d7e874](https://github.com/open-rpc/generator/commit/7d7e87475e96e638db0d97f526e8cb42c30c5e52))
310 | * update server dockerfile ([9b53f10](https://github.com/open-rpc/generator/commit/9b53f1087db17910198462e2ce6736074cd22237))
311 |
312 | ## [1.15.1](https://github.com/open-rpc/generator/compare/1.15.0...1.15.1) (2020-02-10)
313 |
314 |
315 | ### Bug Fixes
316 |
317 | * remove rust install ([d7bdf8e](https://github.com/open-rpc/generator/commit/d7bdf8ef8d359c88d234d69ecc741eee2f1ce5de))
318 | * update circle config ([8745990](https://github.com/open-rpc/generator/commit/8745990cd56e5e0466d476d07540a7aaba07e9cc))
319 | * update semantic-release file ([92e650f](https://github.com/open-rpc/generator/commit/92e650f61650f4f8803009b91b4a6b052d59f678))
320 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenRPC Generator
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | A Generator tool for [open-rpc](https://github.com/open-rpc/spec) APIs.
14 |
15 | Need help or have a question? Join us on [Discord](https://discord.gg/gREUKuF)!
16 |
17 | ## Features:
18 |
19 | - Built in components for:
20 | - Clients
21 | - Server
22 | - Documentation
23 | - Easy to create new components
24 |
25 |
26 | ## Usage
27 |
28 | The generator CLI has a generate command which takes a config to run. The config specifies what components you want to make, as well as the configuration for each component.
29 |
30 | Using the CLI's `init` command, you can walk though an interactive config builder.
31 |
32 | ### Quick start
33 |
34 | ```sh
35 | npm install -g @open-rpc/generator
36 |
37 | open-rpc-generator init
38 | open-rpc-generator generate -c open-rpc-generator-config.json
39 | ```
40 |
41 | ### Generating an individual component
42 |
43 | ```shell
44 | open-rpc-generator generate \
45 | -t client \
46 | -l typescript \
47 | -n petstoreClientTs \
48 | -d https://raw.githubusercontent.com/open-rpc/examples/master/service-descriptions/petstore-openrpc.json \
49 | -o ./generated
50 | ```
51 | ### Custom Component Generation Configuration
52 | Here for customComponent we specify the module that exports as
53 | default the type IComponentModule see custom-test-component.js as an example. It is easy to also refer to an npm package as well as a plain js file. customType is can be anything , it is not restricted to client | server | doc naming.
54 | ```
55 | {
56 | "openrpcDocument": "./src/awesome-custom-client_openrpc.json",
57 | "outDir": "generated-client",
58 | "components": [
59 | {
60 | "type": "custom",
61 | "name": "awesome-custom-client",
62 | "language": "typescript",
63 | "customComponent": "./src/custom-test-component.js",
64 | "customType": "client"
65 | }
66 | ]
67 | }
68 | ```
69 | ## Resources
70 |
71 | - [@open-rpc/generator package](https://www.npmjs.com/package/@open-rpc/generator)
72 | - [example open-rpc documents](https://github.com/open-rpc/examples/tree/master/service-descriptions)
73 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import eslint from '@eslint/js';
2 | import tseslint from '@typescript-eslint/eslint-plugin';
3 | import tsParser from '@typescript-eslint/parser';
4 | import reactPlugin from 'eslint-plugin-react';
5 | import prettierConfig from 'eslint-config-prettier';
6 | import prettierPlugin from 'eslint-plugin-prettier';
7 | import { fileURLToPath } from 'url';
8 | import { dirname } from 'path';
9 | import globals from "globals";
10 |
11 |
12 | const __filename = fileURLToPath(import.meta.url);
13 | const __dirname = dirname(__filename);
14 |
15 | export default [
16 | eslint.configs.recommended,
17 | {
18 | files: ['**/*.{ts,tsx}'],
19 | languageOptions: {
20 | parser: tsParser,
21 | parserOptions: {
22 | project: ['./tsconfig.json'],
23 | tsconfigRootDir: __dirname,
24 | ecmaVersion: 2022,
25 | sourceType: 'module',
26 | ecmaFeatures: {
27 | jsx: true
28 | }
29 | },
30 | globals: {
31 | ...globals.browser,
32 | ...globals.node,
33 | ...globals.es6,
34 | chrome: 'readonly',
35 | browser: 'readonly'
36 | }
37 | },
38 | plugins: {
39 | '@typescript-eslint': tseslint,
40 | 'react': reactPlugin,
41 | 'prettier': prettierPlugin
42 | },
43 | rules: {
44 | ...tseslint.configs['recommended'].rules,
45 | ...reactPlugin.configs.recommended.rules,
46 | '@typescript-eslint/explicit-function-return-type': 'off',
47 | '@typescript-eslint/explicit-module-boundary-types': 'off',
48 | '@typescript-eslint/no-explicit-any': 'warn',
49 | '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
50 | 'no-console': ['warn', { allow: ['warn', 'error'] }],
51 | 'prettier/prettier': 'error'
52 | },
53 | ignores: ["package.json","node_modules/", "**/*/dist/", "build/", "coverage/","*.config.js", "**/*.config.ts", "vitest.config.ts", "vite.config.ts"],
54 | settings: {
55 | react: {
56 | version: 'detect'
57 | }
58 | }
59 | },
60 | prettierConfig
61 | ];
--------------------------------------------------------------------------------
/integration-test.mjs:
--------------------------------------------------------------------------------
1 | import SimpleMathClient from "./generated/client/typescript/build/index.js";
2 | console.log("Running client test");
3 | console.log("imported client: ", SimpleMathClient);
4 |
5 | const client = new SimpleMathClient({
6 | transport: { type: "http", port: 4441, host: "localhost" }
7 | });
8 |
9 | client.addition(2, 2).then((result) => {
10 | if (result !== 4) { process.exit(1); }
11 | console.log("finished with result: ", result);
12 | });
13 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | coverageDirectory: '../coverage',
3 | rootDir: './src',
4 | testEnvironment: 'node',
5 | preset: 'ts-jest',
6 | errorOnDeprecated: true,
7 | };
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@open-rpc/generator",
3 | "private": false,
4 | "version": "1.0.34",
5 | "description": "",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/open-rpc/generator.git"
9 | },
10 | "main": "./build/index.js",
11 | "bin": {
12 | "open-rpc-generator": "build/cli.js"
13 | },
14 | "files": [
15 | "build/**/*",
16 | "templates/**/*",
17 | ".node-version"
18 | ],
19 | "scripts": {
20 | "start": "npm run build && ./build/cli.js",
21 | "test": "npm run lint && npm run test:unit && npm run test:integration",
22 | "lint": "eslint 'src/**/*.{ts,tsx}'",
23 | "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
24 | "test:integration": "npm run build && ./build/cli.js generate -c test-generator-config.json && ((cd ./generated/server/typescript/ && npm install && npm start) & (cd ./generated/client/typescript && npm install && npm run build && cd - && sleep 15 && node ./integration-test.mjs))",
25 | "build": "npm run build:clean && tsc && chmod +x build/cli.js",
26 | "build:clean": "rm -rf build",
27 | "watch:build": "tsc --watch",
28 | "watch:test": "jest --watch"
29 | },
30 | "author": "",
31 | "license": "Apache-2.0",
32 | "dependencies": {
33 | "@iarna/toml": "^2.2.5",
34 | "@open-rpc/typings": "^1.12.4",
35 | "commander": "^7.2.0",
36 | "fs-extra": "^11.2.0",
37 | "inquirer": "^12.4.2",
38 | "lodash": "^4.17.21"
39 | },
40 | "devDependencies": {
41 | "@eslint/js": "9.21.0",
42 | "@open-rpc/examples": "^1.7.2",
43 | "@open-rpc/meta-schema": "^1.14.9",
44 | "@open-rpc/schema-utils-js": "^2.0.2",
45 | "@open-rpc/server-js": "^1.9.5",
46 | "@types/connect": "^3.4.38",
47 | "@types/cors": "^2.8.17",
48 | "@types/fs-extra": "^9.0.7",
49 | "@types/jest": "^29.5.12",
50 | "@types/lodash": "^4.17.1",
51 | "@typescript-eslint/eslint-plugin": "^8.25.0",
52 | "eslint-config-prettier": "^10.0.1",
53 | "eslint-plugin-prettier": "^5.2.3",
54 | "eslint-plugin-react": "^7.37.4",
55 | "jest": "29.7.0",
56 | "prettier": "^3.5.2",
57 | "ts-jest": "^29.1.2",
58 | "typescript": "^4.9.5"
59 | }
60 | }
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import program from 'commander';
3 | import orpcGenerator, { IGeneratorOptions } from './index';
4 | import { input, checkbox } from '@inquirer/prompts';
5 | import { parseOpenRPCDocument } from '@open-rpc/schema-utils-js';
6 | import { capitalize } from 'lodash';
7 | import * as fs from 'fs';
8 | import { promisify } from 'util';
9 | const readFile = promisify(fs.readFile);
10 | const writeFile = promisify(fs.writeFile);
11 |
12 | /* eslint-disable-next-line */
13 | const version = require("../package.json").version;
14 |
15 | /* eslint-disable @typescript-eslint/no-explicit-any */
16 | program
17 | .version(version, '-v, --version')
18 | .command('init')
19 | .action(async () => {
20 | // Define a proper type for our answers
21 | interface InitAnswers {
22 | document: string;
23 | outDir: string;
24 | componentTypes: string[];
25 | docsLanguages: string[];
26 | clientLanguages: string[];
27 | serverLanguages: string[];
28 | gatsbyDocsName?: string;
29 | typescriptClientName?: string;
30 | rustClientName?: string;
31 | typescriptServerName?: string;
32 | [key: string]: any; // Allow dynamic access with string indices
33 | }
34 |
35 | // Use sequential prompts instead of an array
36 | const document = await input({
37 | message: 'Where is your OpenRPC document? May be a file path or url.',
38 | default: 'openrpc.json',
39 | validate: async (d: string) => {
40 | try {
41 | await parseOpenRPCDocument(d);
42 | } catch (e: any) {
43 | return `Invalid document. The error recieved: ${e.message}`;
44 | }
45 | return true;
46 | },
47 | });
48 |
49 | const outDir = await input({
50 | message: 'Where would you like to write the generated artifacts?',
51 | default: './',
52 | });
53 |
54 | const componentTypes = await checkbox({
55 | message: 'Which components would you like to generate?',
56 | choices: [
57 | { name: 'client', value: 'client' },
58 | { name: 'server', value: 'server' },
59 | { name: 'docs', value: 'docs' },
60 | ],
61 | });
62 |
63 | // Initialize the answers object
64 | const initAnswers: InitAnswers = {
65 | document,
66 | outDir,
67 | componentTypes,
68 | docsLanguages: [],
69 | clientLanguages: [],
70 | serverLanguages: [],
71 | };
72 |
73 | // Conditional prompts based on component types
74 | if (componentTypes.includes('docs')) {
75 | initAnswers.docsLanguages = await checkbox({
76 | message: 'What type of documentation do you want to generate?',
77 | choices: [{ name: 'gatsby', value: 'gatsby' }],
78 | });
79 |
80 | if (initAnswers.docsLanguages.includes('gatsby')) {
81 | initAnswers.gatsbyDocsName = await input({
82 | message: 'What would you like the gatsby based docs package to be named?',
83 | });
84 | }
85 | }
86 |
87 | if (componentTypes.includes('client')) {
88 | initAnswers.clientLanguages = await checkbox({
89 | message: 'What language(s) would you like to generate a client for?',
90 | choices: [
91 | { name: 'typescript', value: 'typescript' },
92 | { name: 'rust', value: 'rust' },
93 | ],
94 | });
95 |
96 | if (initAnswers.clientLanguages.includes('typescript')) {
97 | initAnswers.typescriptClientName = await input({
98 | message: 'What would you like the typescript client package to be named?',
99 | });
100 | }
101 |
102 | if (initAnswers.clientLanguages.includes('rust')) {
103 | initAnswers.rustClientName = await input({
104 | message: 'What would you like the rust client crate to be named?',
105 | });
106 | }
107 | }
108 |
109 | if (componentTypes.includes('server')) {
110 | initAnswers.serverLanguages = await checkbox({
111 | message: 'What language(s) would you like to generate a server for?',
112 | choices: [{ name: 'typescript', value: 'typescript' }],
113 | });
114 |
115 | if (initAnswers.serverLanguages.includes('typescript')) {
116 | initAnswers.typescriptServerName = await input({
117 | message: 'What would you like the typescript server package to be named?',
118 | });
119 | }
120 | }
121 |
122 | /* eslint-enable @typescript-eslint/no-explicit-any */
123 |
124 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
125 | const components: any = [];
126 |
127 | // eslint-disable-next-line no-console
128 | console.log('Here is a summary of your Generator configuration:');
129 | // eslint-disable-next-line no-console
130 | console.log(JSON.stringify(initAnswers, undefined, '\t'));
131 |
132 | initAnswers.componentTypes.forEach((componentType: string) => {
133 | const languagesKey = `${componentType}Languages` as keyof InitAnswers;
134 | // Add a type guard to ensure the property exists and is an array
135 | const languages = initAnswers[languagesKey];
136 | if (Array.isArray(languages)) {
137 | languages.forEach((language: string) => {
138 | const nameKey = `${language}${capitalize(componentType)}Name` as keyof InitAnswers;
139 | components.push({
140 | type: componentType,
141 | name: initAnswers[nameKey],
142 | language,
143 | });
144 | });
145 | }
146 | });
147 |
148 | const config = {
149 | openrpcDocument: initAnswers.document,
150 | outDir: initAnswers.outDir,
151 | components,
152 | };
153 |
154 | // eslint-disable-next-line no-console
155 | console.log('Writing your config...');
156 | await writeFile(
157 | './open-rpc-generator-config.json',
158 | JSON.stringify(config, undefined, ' '),
159 | 'utf8'
160 | );
161 | // eslint-disable-next-line no-console
162 | console.log(
163 | 'Config created at open-rpc-generator-config.json. To generate components for the first time run:'
164 | );
165 | // eslint-disable-next-line no-console
166 | console.log('open-rpc-generator generate -c ./open-rpc-generator-config.json ');
167 | });
168 |
169 | program
170 | .command('generate')
171 | .option(
172 | '-d, --document [openrpcDocument]',
173 | 'JSON string or a Path/Url pointing to an open rpc schema',
174 | './openrpc.json'
175 | )
176 | .option(
177 | '-o, --outputDir [outputDirectory]',
178 | 'output directory that the clients will be generated into',
179 | './'
180 | )
181 | .option(
182 | '-c, --config [generatorConfigPath]',
183 | 'Path to a JSON file with declarative generator config'
184 | )
185 | .option('-t, --type [type]', 'component type')
186 | .option('-l, --language [language]', 'component language')
187 | .option('-n, --useName [useName]', 'Name to use for the generated component')
188 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
189 | .action(async (opts: any) => {
190 | const outDir = opts.outputDir || process.cwd();
191 |
192 | let config = {
193 | openrpcDocument: opts.document,
194 | outDir,
195 | components: [],
196 | } as IGeneratorOptions;
197 |
198 | if (opts.config) {
199 | config = {
200 | ...config,
201 | ...JSON.parse(await readFile(opts.config, 'utf8')),
202 | };
203 | } else {
204 | config.components.push({
205 | type: opts.type,
206 | name: opts.useName,
207 | language: opts.language,
208 | });
209 | }
210 |
211 | try {
212 | await orpcGenerator(config);
213 | } catch (e) {
214 | console.error('There was error at generator runtime:');
215 | console.error(e);
216 | process.exit(1);
217 | }
218 |
219 | // eslint-disable-next-line no-console
220 | console.log('Done!');
221 | });
222 |
223 | program.parseAsync(process.argv);
224 |
--------------------------------------------------------------------------------
/src/components/client.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { move } from 'fs-extra';
3 | import { IHooks } from './types';
4 | import { readFile } from 'fs-extra';
5 | import * as fs from 'fs';
6 | import { promisify } from 'util';
7 | const writeFile = promisify(fs.writeFile);
8 | import TOML from '@iarna/toml';
9 | import { template } from 'lodash';
10 |
11 | const tsTemplate = template(`
12 | // Code generated by @open-rpc/generator DO NOT EDIT.
13 | import {
14 | RequestManager,
15 | PostMessageWindowTransport,
16 | PostMessageIframeTransport,
17 | WebSocketTransport,
18 | HTTPTransport,
19 | Client,
20 | JSONRPCError,
21 | } from "@open-rpc/client-js";
22 | import _ from "lodash";
23 | import {
24 | OpenrpcDocument as OpenRPC,
25 | MethodObject,
26 | } from "@open-rpc/meta-schema";
27 | import {
28 | MethodCallValidator,
29 | MethodNotFoundError,
30 | parseOpenRPCDocument,
31 | } from "@open-rpc/schema-utils-js";
32 |
33 | <%= methodTypings.toString("typescript") %>
34 |
35 | export interface Options {
36 | transport: {
37 | type: "websocket" | "http" | "https" | "postmessagewindow" | "postmessageiframe";
38 | host: string;
39 | port: number;
40 | path?: string;
41 | protocol?: string;
42 | };
43 | }
44 |
45 | export class <%= className %> {
46 | public rpc: Client;
47 | public static openrpcDocument: OpenRPC = <%= JSON.stringify(openrpcDocument) %>;
48 | public dereffedDocument: OpenRPC | undefined;
49 | public transport:
50 | | HTTPTransport
51 | | WebSocketTransport
52 | | PostMessageWindowTransport
53 | | PostMessageIframeTransport;
54 | private validator: MethodCallValidator | undefined;
55 | private timeout: number | undefined;
56 |
57 | constructor(options: Options) {
58 | if (
59 | options.transport === undefined ||
60 | options.transport.type === undefined
61 | ) {
62 | throw new Error("Invalid constructor params");
63 | }
64 | const { type, host, port, protocol } = options.transport;
65 | let path = options.transport.path || "";
66 | if (path && path[0] !== "/") {
67 | path = "/" + path;
68 | }
69 | switch (type) {
70 | case 'http':
71 | case 'https':
72 | this.transport = new HTTPTransport(
73 | (protocol || type) + "://" + host + ":" + port + path
74 | );
75 | break;
76 | case 'websocket':
77 | this.transport = new WebSocketTransport(
78 | (protocol || "ws://") + host + ":" + port + path
79 | );
80 | break;
81 | case 'postmessageiframe':
82 | this.transport = new PostMessageIframeTransport(
83 | protocol + "://" + host + ":" + port + path
84 | );
85 | break;
86 | case 'postmessagewindow':
87 | this.transport = new PostMessageWindowTransport(
88 | protocol + "://" + host + ":" + port + path
89 | );
90 | break;
91 | default:
92 | throw new Error("unsupported transport");
93 | }
94 | this.rpc = new Client(new RequestManager([this.transport]));
95 | }
96 |
97 | /**
98 | * Adds a JSONRPC notification handler to handle receiving notifications.
99 | * @example
100 | * myClient.onNotification((data)=>console.log(data));
101 | */
102 | private async initialize() {
103 | if (this.validator) {
104 | return;
105 | }
106 | this.dereffedDocument = await parseOpenRPCDocument(<%= className %>.openrpcDocument);
107 | this.validator = new MethodCallValidator(this.dereffedDocument);
108 | }
109 |
110 | /**
111 | * Adds a JSONRPC notification handler to handle receiving notifications.
112 | * @example
113 | * myClient.onNotification((data)=>console.log(data));
114 | */
115 | public onNotification(callback: (data: any) => void) {
116 | this.rpc.onNotification(callback);
117 | }
118 |
119 | /**
120 | * Adds an optional JSONRPCError handler to handle receiving errors that cannot be resolved to a specific request
121 | * @example
122 | * myClient.onError((err: JSONRPCError)=>console.log(err.message));
123 | */
124 | public onError(callback: (data: JSONRPCError) => void) {
125 | this.rpc.onError(callback);
126 | }
127 |
128 | /**
129 | * Sets a default timeout in ms for all requests excluding notifications.
130 | * @example
131 | * // 20s timeout
132 | * myClient.setDefaultTimeout(20000);
133 | * // Removes timeout from request
134 | * myClient.setDefaultTimeout(undefined);
135 | */
136 | public setDefaultTimeout(ms?: number) {
137 | this.timeout = ms;
138 | }
139 |
140 | /**
141 | * Initiates [[<%= className %>.startBatch]] in order to build a batch call.
142 | *
143 | * Subsequent calls to [[<%= className %>.request]] will be added to the batch.
144 | * Once [[<%= className %>.stopBatch]] is called, the promises for the [[<%= className %>.request]]
145 | * will then be resolved. If there is already a batch in progress this method is a noop.
146 | *
147 | * @example
148 | * myClient.startBatch();
149 | * myClient.foo().then(() => console.log("foobar"))
150 | * myClient.bar().then(() => console.log("foobarbaz"))
151 | * myClient.stopBatch();
152 | */
153 | public startBatch(): void {
154 | return this.rpc.startBatch();
155 | }
156 |
157 | /**
158 | * Initiates [[Client.stopBatch]] in order to finalize and send the batch to the underlying transport.
159 | *
160 | * stopBatch will send the [[<%= className %>]] calls made since the last [[<%= className %>.startBatch]] call. For
161 | * that reason, [[<%= className %>.startBatch]] MUST be called before [[<%= className %>.stopBatch]].
162 | *
163 | * @example
164 | * myClient.startBatch();
165 | * myClient.foo().then(() => console.log("foobar"))
166 | * myClient.bar().then(() => console.log("foobarbaz"))
167 | * myClient.stopBatch();
168 | */
169 | public stopBatch(): void {
170 | return this.rpc.stopBatch();
171 | }
172 |
173 | private async request(methodName: string, params: any[]): Promise {
174 | await this.initialize();
175 | if (this.validator === undefined) {
176 | throw new Error("internal error");
177 | }
178 | const methodObject = _.find(
179 | (<%= className %>.openrpcDocument.methods as MethodObject[]),
180 | ({ name }) => name === methodName
181 | ) as MethodObject;
182 | const notification = methodObject.result ? false : true;
183 | const openRpcMethodValidationErrors = this.validator.validate(
184 | methodName,
185 | params
186 | );
187 | if (
188 | openRpcMethodValidationErrors instanceof MethodNotFoundError ||
189 | openRpcMethodValidationErrors.length > 0
190 | ) {
191 | return Promise.reject(openRpcMethodValidationErrors);
192 | }
193 |
194 | let rpcParams;
195 | if (
196 | methodObject.paramStructure &&
197 | methodObject.paramStructure === "by-name"
198 | ) {
199 | rpcParams = _.zipObject(_.map(methodObject.params, "name"), params);
200 | } else {
201 | rpcParams = params;
202 | }
203 | if (notification) {
204 | return this.rpc.notify({ method: methodName, params: rpcParams });
205 | }
206 | return this.rpc.request(
207 | { method: methodName, params: rpcParams },
208 | this.timeout
209 | );
210 | }
211 |
212 | <% openrpcDocument.methods.forEach((method) => { %>
213 | /**
214 | * <%= method.summary %>
215 | */
216 | // tslint:disable-next-line:max-line-length
217 | public <%= method.name %>: <%= methodTypings.getTypingNames("typescript", method).method %> = (...params) => {
218 | return this.request("<%= method.name %>", params);
219 | }
220 | <% }); %>
221 | }
222 | export default <%= className %>;
223 | `);
224 |
225 | const rsTemplate = template(`
226 | #[macro_use]
227 | extern crate jsonrpc_client_core;
228 |
229 | <%= methodTypings.toString("rust", { includeSchemaTypings: true, includeMethodAliasTypings: false }) %>
230 |
231 | jsonrpc_client!(pub struct <%= className %> {
232 | <%= methodTypings.toString("rust", { includeSchemaTypings: false, includeMethodAliasTypings: true }) %>
233 | });
234 | `);
235 |
236 | const hooks: IHooks = {
237 | afterCopyStatic: [
238 | async (dest, frm, component): Promise => {
239 | if (component.language === 'typescript') {
240 | return await move(path.join(dest, '_package.json'), path.join(dest, 'package.json'), {
241 | overwrite: true,
242 | });
243 | }
244 | },
245 | ],
246 | afterCompileTemplate: [
247 | async (dest, frm, component, openrpcDocument): Promise => {
248 | if (component.language === 'typescript') {
249 | const packagePath = path.join(dest, 'package.json');
250 | const fileContents = await readFile(packagePath);
251 | const pkg = JSON.parse(fileContents.toString());
252 | const updatedPkg = JSON.stringify({
253 | ...pkg,
254 | name: component.name,
255 | version: openrpcDocument.info.version,
256 | });
257 |
258 | return await writeFile(packagePath, updatedPkg);
259 | }
260 |
261 | if (component.language === 'rust') {
262 | const cargoTOMLPath = path.join(dest, 'Cargo.toml');
263 | const fileContents = await readFile(cargoTOMLPath);
264 | const cargoTOML = TOML.parse(fileContents.toString());
265 | const updatedCargo = TOML.stringify({
266 | ...cargoTOML,
267 | package: {
268 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
269 | ...(cargoTOML.package as any),
270 | name: component.name,
271 | version: openrpcDocument.info.version,
272 | },
273 | });
274 | return await writeFile(cargoTOMLPath, updatedCargo);
275 | }
276 | },
277 | ],
278 | templateFiles: {
279 | typescript: [
280 | {
281 | path: 'src/index.ts',
282 | template: tsTemplate,
283 | },
284 | ],
285 | rust: [
286 | {
287 | path: 'src/index.rs',
288 | template: rsTemplate,
289 | },
290 | ],
291 | },
292 | };
293 |
294 | export default hooks;
295 |
--------------------------------------------------------------------------------
/src/components/docs.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { remove } from 'fs-extra';
3 | import { IHooks } from './types';
4 | import * as fs from 'fs';
5 | import { promisify } from 'util';
6 | import { template, startCase } from 'lodash';
7 | const writeFile = promisify(fs.writeFile);
8 | const readFile = promisify(fs.readFile);
9 |
10 | const indexTemplate = template(`import React from 'react';
11 | import { Typography, Box, Button } from '@mui/material';
12 | import Grid from '@mui/material/Grid2';
13 | import { Link } from 'gatsby';
14 | import 'monaco-editor/esm/vs/language/json/json.worker.js';
15 |
16 | const IndexPage = () => {
17 | // For SSR, we need a simpler version
18 | if (typeof window === 'undefined') {
19 | return (
20 |
21 | <%= openrpcDocument.info.title %>
22 | Loading...
23 |
24 | );
25 | }
26 |
27 | // For client-side rendering, we show the full content
28 | return (
29 |
30 |
38 | <%= openrpcDocument.info.title %>
39 | <% if (openrpcDocument.info.description) { %>
40 |
41 | <%= openrpcDocument.info.description %>
42 |
43 | <% } %>
44 |
45 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default IndexPage;
54 | `);
55 |
56 | const gatsbyConfigTemplate = template(`
57 | module.exports = {
58 | pathPrefix: "",
59 | siteMetadata: {
60 | title: '<%= openrpcDocument.info.title %>',
61 | description: '<%= openrpcDocument.info.description %>',
62 | siteUrl: 'https://open-rpc.org',
63 | logoUrl: 'https://raw.githubusercontent.com/open-rpc/design/master/icons/open-rpc-logo-noText/open-rpc-logo-noText%20(PNG)/256x256.png',
64 | primaryColor: '#3f51b5', //material-ui primary color
65 | secondaryColor: '#f50057', //material-ui secondary color
66 | author: '',
67 | menuLinks: [
68 | {
69 | name: 'home',
70 | link: '/',
71 | ignoreNextPrev: true
72 | },
73 | {
74 | name: 'API Documentation',
75 | link: '/api-documentation'
76 | }
77 | ],
78 | footerLinks: [
79 | {
80 | name: 'OpenRPC',
81 | link: 'https://open-rpc.org'
82 | }
83 | ]
84 | },
85 | plugins: [
86 | {
87 | resolve: 'gatsby-plugin-mdx',
88 | options: {
89 | extensions: ['.mdx', '.md'],
90 | gatsbyRemarkPlugins: [
91 | {
92 | resolve: 'gatsby-remark-autolink-headers',
93 | options: {
94 | icon: false,
95 | },
96 | },
97 | ],
98 | },
99 | },
100 | "gatsby-openrpc-theme",
101 | {
102 | resolve: 'gatsby-plugin-manifest',
103 | options: {
104 | name: 'pristine-site',
105 | short_name: 'pristine-site',
106 | start_url: '/',
107 | background_color: 'transparent',
108 | theme_color: '#3f51b5',
109 | display: 'minimal-ui',
110 | icon: 'src/images/gatsby-icon.png', // This path is relative to the root of the site.
111 | },
112 | },
113 | "gatsby-plugin-image",
114 | "gatsby-plugin-sharp",
115 | "gatsby-transformer-sharp",
116 | {
117 | resolve: "gatsby-source-filesystem",
118 | options: {
119 | name: "images",
120 | path: __dirname + '/src/images',
121 | },
122 | },
123 | {
124 | resolve: "gatsby-source-filesystem",
125 | options: {
126 | name: "docs",
127 | path: __dirname + '/src/docs',
128 | },
129 | },
130 | ],
131 | }
132 | `);
133 |
134 | const hooks: IHooks = {
135 | afterCopyStatic: [
136 | async (dest, frm, component, openrpcDocument): Promise => {
137 | const replacePackageJsonContent = async (fileName: string) => {
138 | const destPath = path.join(dest, fileName);
139 | const tmplPath = path.join(dest, `_${fileName}`);
140 |
141 | const tmplPkgStr = await readFile(tmplPath, 'utf8');
142 | let tmplPkg = JSON.parse(tmplPkgStr);
143 |
144 | tmplPkg.name = component.name || startCase(openrpcDocument.info.title).replace(/\s/g, '');
145 | tmplPkg.version = openrpcDocument.info.version;
146 |
147 | let currPkgStr;
148 | try {
149 | currPkgStr = await readFile(destPath, 'utf8');
150 | const currPkg = JSON.parse(currPkgStr);
151 | tmplPkg = {
152 | ...currPkg,
153 | ...tmplPkg,
154 | dependencies: {
155 | ...currPkg.dependencies,
156 | ...tmplPkg.dependencies,
157 | },
158 | devDependencies: {
159 | ...currPkg.devDependencies,
160 | ...tmplPkg.devDependencies,
161 | },
162 | };
163 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
164 | } catch (e) {
165 | // do nothing
166 | }
167 |
168 | await writeFile(destPath, JSON.stringify(tmplPkg, undefined, ' '));
169 | await remove(tmplPath);
170 | };
171 | await replacePackageJsonContent('package.json');
172 | await replacePackageJsonContent('package-lock.json');
173 | },
174 | ],
175 | templateFiles: {
176 | gatsby: [
177 | {
178 | path: 'src/pages/index.tsx',
179 | template: indexTemplate,
180 | },
181 | {
182 | path: 'gatsby-config.js',
183 | template: gatsbyConfigTemplate,
184 | },
185 | ],
186 | },
187 | };
188 |
189 | export default hooks;
190 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | import defaultClientHooks from './client';
2 | import defaultDocHooks from './docs';
3 | import defaultServerHooks from './server';
4 | import { IComponentModule, IStaticPath } from './types';
5 | import path from 'path';
6 | export * from './types';
7 |
8 | export const getDefaultComponentTemplatePath: IStaticPath = (language: string, type?: string) => {
9 | const d = `/templates/${type}/${language}/`;
10 | return path.join(__dirname, '../../', d);
11 | };
12 |
13 | export const defaultClientComponent: IComponentModule = {
14 | hooks: defaultClientHooks,
15 | staticPath: getDefaultComponentTemplatePath,
16 | };
17 |
18 | export const defaultDocComponent: IComponentModule = {
19 | hooks: defaultDocHooks,
20 | staticPath: getDefaultComponentTemplatePath,
21 | };
22 |
23 | export const defaultServerComponent: IComponentModule = {
24 | hooks: defaultServerHooks,
25 | staticPath: getDefaultComponentTemplatePath,
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/server.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { ensureDir, remove } from 'fs-extra';
3 | import { IHooks } from './types';
4 | import * as fs from 'fs';
5 | import { promisify } from 'util';
6 | import { template } from 'lodash';
7 | import {
8 | ContentDescriptorObject,
9 | ExamplePairingObject,
10 | ExampleObject,
11 | MethodObject,
12 | } from '@open-rpc/meta-schema';
13 | const writeFile = promisify(fs.writeFile);
14 | const readFile = promisify(fs.readFile);
15 | const access = promisify(fs.access);
16 |
17 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
18 | const onlyHandleTS = ({ language }: any) => {
19 | if (language !== 'typescript') {
20 | throw new Error('Cannot handle any other language other than TS for server generator');
21 | }
22 | };
23 |
24 | const methodMappingTemplate =
25 | template(`// Code generated by @open-rpc/generator DO NOT EDIT or ur gonna have a bad tiem
26 | import { MethodMapping } from "@open-rpc/server-js/build/router";
27 |
28 | import methods from "./methods";
29 |
30 | export const methodMapping: MethodMapping = {
31 | <% openrpcDocument.methods.forEach(({ name }) => { %> <%= name %>: methods.<%= name %>,
32 | <% }); %>};
33 |
34 | export default methodMapping;
35 | `);
36 |
37 | const generatedTypingsTemplate = template(`<%= methodTypings.toString("typescript") %>`);
38 |
39 | const hooks: IHooks = {
40 | afterCopyStatic: [
41 | async (dest, frm, component, openrpcDocument): Promise => {
42 | onlyHandleTS(component);
43 | const destPath = path.join(dest, 'package.json');
44 | const tmplPath = path.join(dest, '_package.json');
45 |
46 | const tmplPkgStr = await readFile(tmplPath, 'utf8');
47 | let tmplPkg = JSON.parse(tmplPkgStr);
48 |
49 | tmplPkg.name = component.name || openrpcDocument.info.title;
50 | tmplPkg.version = openrpcDocument.info.version;
51 |
52 | let currPkgStr;
53 | try {
54 | currPkgStr = await readFile(destPath, 'utf8');
55 | const currPkg = JSON.parse(currPkgStr);
56 | tmplPkg = {
57 | ...currPkg,
58 | ...tmplPkg,
59 | scripts: {
60 | ...currPkg.scripts,
61 | ...tmplPkg.scripts,
62 | },
63 | dependencies: {
64 | ...currPkg.dependencies,
65 | ...tmplPkg.dependencies,
66 | },
67 | devDependencies: {
68 | ...currPkg.devDependencies,
69 | ...tmplPkg.devDependencies,
70 | },
71 | };
72 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
73 | } catch (e) {
74 | // do nothing
75 | }
76 |
77 | await writeFile(destPath, JSON.stringify(tmplPkg, undefined, ' '));
78 | await remove(tmplPath);
79 | },
80 | ],
81 | afterCompileTemplate: [
82 | async (dest, frm, component, openrpcDocument, typings, dereffedDocument): Promise => {
83 | onlyHandleTS(component);
84 |
85 | const methodsFolder = `${dest}/src/methods/`;
86 | await ensureDir(methodsFolder);
87 |
88 | // Only write new one if there isnt one already.
89 | for (const method of dereffedDocument.methods as MethodObject[]) {
90 | const methodFileName = `${methodsFolder}/${method.name}.ts`;
91 |
92 | const functionAliasName = typings.getTypingNames('typescript', method).method;
93 | const params = method.params as ContentDescriptorObject[];
94 | const functionParams = params.map(({ name }) => name).join(', ');
95 |
96 | const newFunctionInterface = `const ${method.name}: ${functionAliasName} = (${functionParams}) => {`;
97 |
98 | let exists = true;
99 | try {
100 | await access(methodFileName, fs.constants.F_OK);
101 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
102 | } catch (e) {
103 | exists = false;
104 | }
105 |
106 | let codeToWrite = '';
107 | if (exists) {
108 | const existingMethod = await readFile(methodFileName, 'utf8');
109 | /* eslint-disable-next-line */
110 | const methodRegExp = new RegExp(`const ${method.name}: ${functionAliasName} = \(.*\) =>`, "gm");
111 | existingMethod.replace(methodRegExp, newFunctionInterface);
112 | codeToWrite = existingMethod;
113 | } else {
114 | let returnVal = '';
115 | if (method.examples) {
116 | const example = method.examples[0] as ExamplePairingObject;
117 | const exRes = example.result as ExampleObject;
118 | returnVal = exRes.value;
119 | }
120 |
121 | codeToWrite = [
122 | `import { ${functionAliasName} } from "../generated-typings";`,
123 | '',
124 | newFunctionInterface,
125 | ` return Promise.resolve(${returnVal});`,
126 | `};`,
127 | '',
128 | `export default ${method.name};`,
129 | '',
130 | ].join('\n');
131 | }
132 |
133 | await writeFile(methodFileName, codeToWrite, 'utf8');
134 | }
135 |
136 | const methods = openrpcDocument.methods as MethodObject[];
137 | const imports = methods.map(({ name }) => `import ${name} from "./${name}";`);
138 | const methodMappingStr = [
139 | 'const methods = {',
140 | ...methods.map(({ name }) => ` ${name},`),
141 | '};',
142 | ];
143 |
144 | const defaultExportStr = 'export default methods;';
145 |
146 | await writeFile(
147 | `${methodsFolder}/index.ts`,
148 | [...imports, '', ...methodMappingStr, '', defaultExportStr, ''].join('\n'),
149 | 'utf8'
150 | );
151 | },
152 | ],
153 | templateFiles: {
154 | typescript: [
155 | {
156 | path: 'src/generated-method-mapping.ts',
157 | template: methodMappingTemplate,
158 | },
159 | {
160 | path: 'src/generated-typings.ts',
161 | template: generatedTypingsTemplate,
162 | },
163 | ],
164 | },
165 | };
166 |
167 | export default hooks;
168 |
--------------------------------------------------------------------------------
/src/components/types.ts:
--------------------------------------------------------------------------------
1 | import { TemplateExecutor } from 'lodash';
2 | import { OpenrpcDocument as OpenRPC } from '@open-rpc/meta-schema';
3 | import Typings from '@open-rpc/typings';
4 |
5 | interface IComponent {
6 | hooks: IHooks;
7 | type: string;
8 | name: string;
9 | language: string;
10 | staticPath?: string;
11 | }
12 | export type FHook = (
13 | destDir: string,
14 | fromDir: string | undefined,
15 | component: IComponent,
16 | openrpcDocument: OpenRPC,
17 | Typings: Typings,
18 | dereffedDocument: OpenRPC
19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
20 | ) => Promise;
21 |
22 | export interface IHooks {
23 | beforeCopyStatic?: FHook[];
24 | afterCopyStatic?: FHook[];
25 | beforeCompileTemplate?: FHook[];
26 | afterCompileTemplate?: FHook[];
27 | templateFiles: {
28 | [key: string]: {
29 | path: string;
30 | template: TemplateExecutor;
31 | }[];
32 | };
33 | }
34 |
35 | export type IStaticPath = (language: string, type?: string) => string | undefined;
36 |
37 | export interface IComponentModule {
38 | hooks: IHooks;
39 | staticPath: IStaticPath;
40 | }
41 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | export interface IClientConfig {
2 | type: 'client';
3 | name: string;
4 | language: 'typescript' | 'rust';
5 | outPath?: string;
6 | }
7 |
8 | export interface IServerConfig {
9 | type: 'server';
10 | name: string;
11 | language: 'typescript';
12 | outPath?: string;
13 | }
14 |
15 | export interface IDocsConfig {
16 | type: 'docs';
17 | name: string;
18 | language: 'gatsby';
19 | outPath?: string;
20 | }
21 |
22 | // Experimental
23 | export interface ICustomConfig {
24 | type: 'custom';
25 | name: string;
26 | customComponent: string;
27 | customType?: string;
28 | openRPCPath?: string | null;
29 | outPath?: string;
30 | language: 'typescript' | 'rust';
31 | }
32 |
33 | export type TComponentConfig = IClientConfig | IServerConfig | IDocsConfig | ICustomConfig;
34 |
--------------------------------------------------------------------------------
/src/custom-test-component.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const { move, readFile, writeFile } =require("fs-extra");
3 | const _ = require('lodash')
4 | const components = require("./components")
5 | const { getDefaultComponentTemplatePath } = components;
6 |
7 | const { template } = _;
8 | const tsTemplate = template(`
9 | // Code generated by @custom-test generator DO NOT EDIT.
10 | import { RequestManager, PostMessageWindowTransport, PostMessageIframeTransport, WebSocketTransport, HTTPTransport, Client, JSONRPCError } from "@open-rpc/client-js";
11 | import _ from "lodash";
12 | import { OpenrpcDocument as OpenRPC, MethodObject, ContentDescriptorObject } from "@open-rpc/meta-schema";
13 | import { MethodCallValidator, MethodNotFoundError } from "@open-rpc/schema-utils-js";
14 |
15 | <%= methodTypings.toString("typescript") %>
16 |
17 | export interface Options {
18 | transport: {
19 | type: "websocket" | "http" | "https" | "postmessagewindow" | "postmessageiframe";
20 | host: string;
21 | port: number;
22 | path?: string;
23 | protocol?: string;
24 | },
25 | }
26 |
27 | export class <%= className %> {
28 | public rpc: Client;
29 | public static openrpcDocument: OpenRPC = <%= JSON.stringify(openrpcDocument) %> ;
30 | public transport: HTTPTransport | WebSocketTransport | PostMessageWindowTransport | PostMessageIframeTransport;
31 | private validator: MethodCallValidator;
32 | private timeout: number | undefined;
33 |
34 | constructor(options: Options) {
35 |
36 | if (options.transport === undefined || options.transport.type === undefined) {
37 | throw new Error("Invalid constructor params");
38 | }
39 | const {type, host, port, protocol} = options.transport;
40 | let path = options.transport.path || "";
41 | if(path && path[0] !== "/") {
42 | path = "/" + path;
43 | }
44 | switch (type) {
45 | case 'http':
46 | case 'https':
47 | this.transport = new HTTPTransport((protocol || type) + "://" + host + ":" + port + path);
48 | break;
49 | case 'websocket':
50 | this.transport = new WebSocketTransport((protocol || "ws://") + host + ":" + port + path);
51 | break;
52 | case 'postmessageiframe':
53 | this.transport = new PostMessageIframeTransport(protocol + "://" + host + ":" + port + path);
54 | break;
55 | case 'postmessagewindow':
56 | this.transport = new PostMessageWindowTransport(protocol + "://" + host + ":" + port + path);
57 | break;
58 | default:
59 | throw new Error("unsupported transport");
60 | break;
61 | }
62 | this.rpc = new Client(new RequestManager([this.transport]));
63 | this.validator = new MethodCallValidator(<%= className %>.openrpcDocument);
64 | }
65 | /**
66 | * Adds a JSONRPC notification handler to handle receiving notifications.
67 | * @example
68 | * myClient.onNotification((data)=>console.log(data));
69 | */
70 | public onNotification(callback: (data: any) => void) {
71 | this.rpc.onNotification(callback);
72 | }
73 |
74 | /**
75 | * Adds an optional JSONRPCError handler to handle receiving errors that cannot be resolved to a specific request
76 | * @example
77 | * myClient.onError((err: JSONRPCError)=>console.log(err.message));
78 | */
79 | public onError(callback: (data: JSONRPCError) => void) {
80 | this.rpc.onError(callback);
81 | }
82 |
83 | /**
84 | * Sets a default timeout in ms for all requests excluding notifications.
85 | * @example
86 | * // 20s timeout
87 | * myClient.setDefaultTimeout(20000);
88 | * // Removes timeout from request
89 | * myClient.setDefaultTimeout(undefined);
90 | */
91 | public setDefaultTimeout(ms?: number) {
92 | this.timeout = ms;
93 | }
94 |
95 | /**
96 | * Initiates [[<%= className %>.startBatch]] in order to build a batch call.
97 | *
98 | * Subsequent calls to [[<%= className %>.request]] will be added to the batch.
99 | * Once [[<%= className %>.stopBatch]] is called, the promises for the [[<%= className %>.request]]
100 | * will then be resolved. If there is already a batch in progress this method is a noop.
101 | *
102 | * @example
103 | * myClient.startBatch();
104 | * myClient.foo().then(() => console.log("foobar"))
105 | * myClient.bar().then(() => console.log("foobarbaz"))
106 | * myClient.stopBatch();
107 | */
108 | public startBatch(): void {
109 | return this.rpc.startBatch();
110 | }
111 |
112 | /**
113 | * Initiates [[Client.stopBatch]] in order to finalize and send the batch to the underlying transport.
114 | *
115 | * stopBatch will send the [[<%= className %>]] calls made since the last [[<%= className %>.startBatch]] call. For
116 | * that reason, [[<%= className %>.startBatch]] MUST be called before [[<%= className %>.stopBatch]].
117 | *
118 | * @example
119 | * myClient.startBatch();
120 | * myClient.foo().then(() => console.log("foobar"))
121 | * myClient.bar().then(() => console.log("foobarbaz"))
122 | * myClient.stopBatch();
123 | */
124 | public stopBatch(): void {
125 | return this.rpc.stopBatch();
126 | }
127 |
128 | private request(methodName: string, params: any[]): Promise {
129 | const methodObject = _.find((<%= className %>.openrpcDocument.methods as MethodObject[]), ({name}) => name === methodName) as MethodObject;
130 | const notification = methodObject.result ? false : true;
131 | const openRpcMethodValidationErrors = this.validator.validate(methodName, params);
132 | if ( openRpcMethodValidationErrors instanceof MethodNotFoundError || openRpcMethodValidationErrors.length > 0) {
133 | return Promise.reject(openRpcMethodValidationErrors);
134 | }
135 |
136 | let rpcParams;
137 | if (methodObject.paramStructure && methodObject.paramStructure === "by-name") {
138 | rpcParams = _.zipObject(params, _.map(methodObject.params, "name"));
139 | } else {
140 | rpcParams = params;
141 | }
142 | if (notification) {
143 | return this.rpc.notify({method: methodName, params: rpcParams});
144 | }
145 | return this.rpc.request({method: methodName, params: rpcParams}, this.timeout);
146 | }
147 |
148 | <% openrpcDocument.methods.forEach((method) => { %>
149 | /**
150 | * <%= method.summary %>
151 | */
152 | // tslint:disable-next-line:max-line-length
153 | public <%= method.name %>: <%= methodTypings.getTypingNames("typescript", method).method %> = (...params) => {
154 | return this.request("<%= method.name %>", params);
155 | }
156 | <% }); %>
157 | }
158 | export default <%= className %>;
159 | `);
160 |
161 |
162 | const hooks = {
163 | afterCopyStatic: [
164 | async (dest, frm, component) => {
165 | if (component.language === "typescript") {
166 | return await move(path.join(dest, "_package.json"), path.join(dest, "package.json"), { overwrite: true });
167 | }
168 | },
169 | ],
170 | afterCompileTemplate: [
171 | async (dest, frm, component, openrpcDocument) => {
172 | if (component.language === "typescript") {
173 | const packagePath = path.join(dest, "package.json");
174 | const fileContents = await readFile(packagePath);
175 | const pkg = JSON.parse(fileContents.toString());
176 | const updatedPkg = JSON.stringify({
177 | ...pkg,
178 | name: component.name,
179 | version: openrpcDocument.info.version,
180 | });
181 |
182 | return await writeFile(packagePath, updatedPkg);
183 | }
184 | },
185 | ],
186 | templateFiles: {
187 | typescript: [
188 | {
189 | path: "src/index.ts",
190 | template: tsTemplate,
191 | },
192 | ],
193 | },
194 | };
195 |
196 |
197 | module.exports = {
198 | hooks,
199 | staticPath: getDefaultComponentTemplatePath
200 | }
201 |
--------------------------------------------------------------------------------
/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import clientGen from './';
2 | import fs from 'fs';
3 | import fsx, { emptyDir } from 'fs-extra';
4 | import examples from '@open-rpc/examples';
5 | import { promisify } from 'util';
6 | import { forEach } from 'lodash';
7 | import { OpenRPCDocumentDereferencingError } from '@open-rpc/schema-utils-js';
8 | import { OpenrpcDocument as OpenRPC } from '@open-rpc/meta-schema';
9 | import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
10 |
11 | const stat = promisify(fs.stat);
12 | const rmdir = promisify(fs.rmdir);
13 | console.error = () => 'noop';
14 |
15 | describe(`Examples to generate Js clients`, () => {
16 | const testDir = `${process.cwd()}/test`;
17 |
18 | beforeAll(async () => {
19 | await emptyDir(testDir);
20 | });
21 |
22 | afterAll(async () => {
23 | await fsx.emptyDir(testDir);
24 | return await rmdir(testDir);
25 | });
26 |
27 | it('fails when the open rpc document is invalid', () => {
28 | const testDocument = {
29 | openrpcDocument: {
30 | openrpc: '1.2.1',
31 | info: {
32 | version: '1',
33 | title: 'test',
34 | },
35 | methods: [
36 | {
37 | name: 'foo',
38 | params: [{ $ref: '#/components/contentDescriptors/LeFoo' }],
39 | result: {
40 | name: 'bar',
41 | schema: { $ref: '#/components/contentDescriptors/LeFoo' },
42 | },
43 | },
44 | ],
45 | components: {
46 | schemas: {
47 | LeBar: { title: 'LeBar', type: 'string' },
48 | },
49 | contentDescriptors: {
50 | LeFoo: {
51 | name: 'LeFoo',
52 | required: true,
53 | schema: { $ref: '#/components/schemas/LeBar' },
54 | },
55 | },
56 | },
57 | } as OpenRPC,
58 | outDir: testDir,
59 | components: [],
60 | };
61 | const genProm = clientGen(testDocument);
62 |
63 | return expect(genProm).rejects.toBeInstanceOf(OpenRPCDocumentDereferencingError);
64 | });
65 | forEach(examples, (example: OpenRPC, exampleName: string) => {
66 | it(`rejects configurations without outDir or outPath`, async () => {
67 | const promGen = clientGen({
68 | openrpcDocument: example,
69 | components: [{ type: 'client', language: 'typescript', name: 'testclient-ts' }],
70 | });
71 | expect(promGen).rejects.toBeInstanceOf(Error);
72 | });
73 |
74 | it(`creates a new client for example: ${exampleName} and regenerates after`, async () => {
75 | const exampleOutDir = `${testDir}/${exampleName}`;
76 | expect.assertions(5);
77 |
78 | await clientGen({
79 | openrpcDocument: example,
80 | outDir: exampleOutDir,
81 | components: [
82 | { type: 'client', language: 'rust', name: 'testclient-rs' },
83 | { type: 'client', language: 'typescript', name: 'testclient-ts' },
84 | { type: 'server', language: 'typescript', name: 'testserver-ts' },
85 | { type: 'docs', language: 'gatsby', name: 'testserver-gatsby' },
86 | {
87 | type: 'custom',
88 | language: 'typescript',
89 | name: 'custom-stuff',
90 | customComponent: './src/custom-test-component.js',
91 | customType: 'client',
92 | },
93 | {
94 | type: 'custom',
95 | language: 'typescript',
96 | name: 'custom-stuff2',
97 | customComponent: './src/custom-test-component.js',
98 | customType: 'client',
99 | openRPCPath: null,
100 | },
101 | {
102 | type: 'custom',
103 | language: 'typescript',
104 | name: 'custom-stuff3',
105 | customComponent: './src/custom-test-component.js',
106 | customType: 'client',
107 | openRPCPath: 'tmpz',
108 | },
109 | {
110 | type: 'custom',
111 | language: 'typescript',
112 | name: 'custom-stuff4',
113 | customComponent: './src/custom-test-component.js',
114 | customType: 'client',
115 | openRPCPath: 'tmpy',
116 | outPath: `${exampleOutDir}/special`,
117 | },
118 | ],
119 | });
120 |
121 | await expect(stat(exampleOutDir)).resolves.toBeTruthy();
122 | await expect(stat(`${exampleOutDir}/special`)).resolves.toBeTruthy();
123 |
124 | await clientGen({
125 | openrpcDocument: example,
126 | outDir: exampleOutDir,
127 | components: [
128 | { type: 'client', language: 'rust', name: 'testclient-rs' },
129 | { type: 'client', language: 'typescript', name: 'testclient-ts' },
130 | { type: 'server', language: 'typescript', name: 'testserver-ts' },
131 | { type: 'docs', language: 'gatsby', name: 'testserver-gatsby' },
132 | {
133 | type: 'custom',
134 | language: 'typescript',
135 | name: 'custom-stuff',
136 | customComponent: './src/custom-test-component.js',
137 | customType: 'client',
138 | },
139 | {
140 | type: 'custom',
141 | language: 'typescript',
142 | name: 'custom-stuff2',
143 | customComponent: './src/custom-test-component.js',
144 | customType: 'client',
145 | openRPCPath: null,
146 | },
147 | {
148 | type: 'custom',
149 | language: 'typescript',
150 | name: 'custom-stuff3',
151 | customComponent: './src/custom-test-component.js',
152 | customType: 'client',
153 | openRPCPath: 'tmpz',
154 | },
155 | {
156 | type: 'custom',
157 | language: 'typescript',
158 | name: 'custom-stuff4',
159 | customComponent: './src/custom-test-component.js',
160 | customType: 'client',
161 | openRPCPath: 'tmpy',
162 | outPath: `${exampleOutDir}/special`,
163 | },
164 | ],
165 | });
166 |
167 | await expect(stat(`${exampleOutDir}/special`)).resolves.toBeTruthy();
168 | await expect(stat(exampleOutDir)).resolves.toBeTruthy();
169 | }, 100000);
170 | });
171 | });
172 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import { ensureDir, copy, move, remove } from 'fs-extra';
3 | import * as path from 'path';
4 | import { promisify } from 'util';
5 | import { startCase } from 'lodash';
6 | import { OpenrpcDocument as OpenRPC } from '@open-rpc/meta-schema';
7 | import { parseOpenRPCDocument } from '@open-rpc/schema-utils-js';
8 | import { TComponentConfig } from './config';
9 | import Typings from '@open-rpc/typings';
10 |
11 | import {
12 | defaultClientComponent,
13 | defaultDocComponent,
14 | defaultServerComponent,
15 | IComponentModule,
16 | IHooks,
17 | FHook,
18 | } from './components';
19 | export * as components from './components';
20 |
21 | const writeFile = promisify(fs.writeFile);
22 |
23 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
24 | const moveFiles = async (dirName: string, file1: string, file2: string): Promise => {
25 | try {
26 | return await move(path.join(dirName, file1), path.join(dirName, file2));
27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
28 | } catch (error) {
29 | return;
30 | }
31 | };
32 |
33 | interface IComponentModules {
34 | [k: string]: IComponentModule;
35 | }
36 |
37 | const componentModules: IComponentModules = {
38 | client: defaultClientComponent,
39 | server: defaultServerComponent,
40 | docs: defaultDocComponent,
41 | };
42 |
43 | interface IComponent {
44 | hooks: IHooks;
45 | type: string;
46 | name: string;
47 | language: string;
48 | staticPath?: string;
49 | openRPCPath?: string;
50 | }
51 |
52 | const getComponentFromConfig = async (componentConfig: TComponentConfig): Promise => {
53 | const { language, name, type } = componentConfig;
54 | let openRPCPath: string | undefined = 'src';
55 | if (componentConfig.type === 'custom') {
56 | const componentPath = componentConfig.customComponent.startsWith('./')
57 | ? path.resolve(process.cwd(), componentConfig.customComponent)
58 | : componentConfig.customComponent;
59 | const compModule: IComponentModule = (await import(componentPath)).default;
60 | if (compModule.hooks === undefined) throw new Error('Hooks interface not exported or defined');
61 | openRPCPath =
62 | componentConfig.openRPCPath === null ? undefined : componentConfig.openRPCPath || 'src';
63 | return {
64 | hooks: compModule.hooks,
65 | staticPath: compModule.staticPath(language, componentConfig.customType),
66 | language,
67 | name,
68 | type,
69 | openRPCPath,
70 | };
71 | }
72 | const componentModule = componentModules[type];
73 | return {
74 | hooks: componentModule.hooks,
75 | staticPath: componentModule.staticPath(language, type),
76 | language,
77 | name,
78 | type,
79 | openRPCPath,
80 | };
81 | };
82 |
83 | const makeApplyHooks = (
84 | hooks: FHook[] | undefined,
85 | openrpcDocument: OpenRPC,
86 | typings: Typings,
87 | dereffedDocument: OpenRPC
88 | ) => {
89 | return async (destDir: string, srcDir: string | undefined, component: IComponent) => {
90 | if (hooks === undefined) return;
91 | if (hooks.length === 0) return;
92 | for (const hookFn of hooks) {
93 | await hookFn(destDir, srcDir, component, openrpcDocument, typings, dereffedDocument);
94 | }
95 | };
96 | };
97 |
98 | const copyStaticForComponent = async (
99 | destinationDirectoryName: string,
100 | component: IComponent,
101 | openrpcDocument: OpenRPC,
102 | typings: Typings,
103 | dereffedDocument: OpenRPC
104 | ) => {
105 | const { staticPath, hooks } = component;
106 | if (staticPath === undefined) return;
107 |
108 | const { beforeCopyStatic, afterCopyStatic } = hooks;
109 | const applyBeforeCopyStatic = makeApplyHooks(
110 | beforeCopyStatic,
111 | openrpcDocument,
112 | typings,
113 | dereffedDocument
114 | );
115 | const applyAfterCopyStatic = makeApplyHooks(
116 | afterCopyStatic,
117 | openrpcDocument,
118 | typings,
119 | dereffedDocument
120 | );
121 |
122 | await applyBeforeCopyStatic(destinationDirectoryName, staticPath, component);
123 | await copy(staticPath, destinationDirectoryName, { overwrite: true, dereference: true });
124 |
125 | // ignores errors incase there is no gitignore...
126 | // gets around an issue with the copy function whereby hidden dotfiles are not copied.
127 | await moveFiles(destinationDirectoryName, 'gitignore', '.gitignore');
128 | await remove(`${destinationDirectoryName}/gitignore`);
129 |
130 | // this is where we would do things like move _package.json to package.json, etc, etc
131 | await applyAfterCopyStatic(destinationDirectoryName, staticPath, component);
132 | };
133 |
134 | export interface IGeneratorOptions {
135 | outDir?: string;
136 | openrpcDocument: OpenRPC | string;
137 | components: TComponentConfig[];
138 | }
139 |
140 | const prepareOutputDirectory = async (outDir: string, component: IComponent): Promise => {
141 | const destinationDirectoryName = `${outDir}/${component.type}/${component.language}`;
142 | const openRPCDefaultLocation = 'src';
143 | await ensureDir(`${destinationDirectoryName}/${openRPCDefaultLocation}`);
144 | return destinationDirectoryName;
145 | };
146 |
147 | const writeOpenRpcDocument = async (
148 | outDir: string,
149 | doc: OpenRPC | string,
150 | component: IComponent
151 | ): Promise => {
152 | if (component.openRPCPath === undefined) return;
153 | const toWrite =
154 | typeof doc === 'string' ? await parseOpenRPCDocument(doc, { dereference: false }) : doc;
155 | const openRPCPath = `${outDir}/${component.openRPCPath}`;
156 | await ensureDir(openRPCPath);
157 | const destinationDirectoryName = `${openRPCPath}/openrpc.json`;
158 | await writeFile(destinationDirectoryName, JSON.stringify(toWrite, undefined, ' '), 'utf8');
159 | return destinationDirectoryName;
160 | };
161 |
162 | const compileTemplate = async (
163 | destDir: string,
164 | component: IComponent,
165 | openrpcDocument: OpenRPC,
166 | typings: Typings,
167 | dereffedDocument: OpenRPC
168 | ): Promise => {
169 | const { hooks } = component;
170 | const { beforeCompileTemplate, afterCompileTemplate } = hooks;
171 |
172 | const applyBeforeCompileTemplate = makeApplyHooks(
173 | beforeCompileTemplate,
174 | openrpcDocument,
175 | typings,
176 | dereffedDocument
177 | );
178 | const applyAfterCompileTemplate = makeApplyHooks(
179 | afterCompileTemplate,
180 | openrpcDocument,
181 | typings,
182 | dereffedDocument
183 | );
184 |
185 | await applyBeforeCompileTemplate(destDir, undefined, component);
186 |
187 | // 1. read files in the templated directory,
188 | // 2. for each one, pass in the template params
189 | const templates = hooks.templateFiles[component.language];
190 | for (const t of templates) {
191 | const result = t.template({
192 | className: startCase(openrpcDocument.info.title).replace(/\s/g, ''),
193 | methodTypings: typings,
194 | openrpcDocument: openrpcDocument,
195 | });
196 |
197 | await writeFile(`${destDir}/${t.path}`, result, 'utf8');
198 | }
199 |
200 | await applyAfterCompileTemplate(destDir, undefined, component);
201 |
202 | return true;
203 | };
204 |
205 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
206 | const clone = (x: any) => JSON.parse(JSON.stringify(x));
207 |
208 | export default async (generatorOptions: IGeneratorOptions) => {
209 | const { openrpcDocument, outDir } = generatorOptions;
210 | let dereffedDocument: OpenRPC;
211 | const doc = await parseOpenRPCDocument(openrpcDocument, { dereference: false });
212 |
213 | try {
214 | dereffedDocument = await parseOpenRPCDocument(clone(doc));
215 | } catch (e) {
216 | console.error('Invalid OpenRPC document. Please revise the validation errors below:'); // tslint:disable-line
217 | console.error(e);
218 | throw e;
219 | }
220 |
221 | const methodTypings = new Typings(dereffedDocument);
222 |
223 | for (const componentConfig of generatorOptions.components) {
224 | const outPath = componentConfig.outPath;
225 | if (outPath === undefined && outDir === undefined) {
226 | console.error('No output path specified');
227 | throw new Error('No output path specified');
228 | }
229 | const component = await getComponentFromConfig(componentConfig);
230 | let destDir = outPath;
231 | if (!outPath) {
232 | destDir = await prepareOutputDirectory(outDir!, component);
233 | } else {
234 | await ensureDir(outPath);
235 | }
236 | await copyStaticForComponent(destDir!, component, doc, methodTypings, dereffedDocument);
237 | await writeOpenRpcDocument(destDir!, doc, component);
238 | await compileTemplate(destDir!, component, doc, methodTypings, dereffedDocument);
239 | }
240 | };
241 |
--------------------------------------------------------------------------------
/templates/client/rust/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "template-client"
3 | description = "JSON-RPC Client"
4 | license = "Apache-2.0"
5 | version = "1.0.0"
6 | authors = ["Zachary Belford ", "Mike Lubinets "]
7 | edition = "2018"
8 |
9 | [dependencies]
10 | jsonrpc-client-core = "0.5.0"
11 | serde = { version = "1.0", features = ["derive"] }
12 | serde_json = "1.0"
13 |
14 | [dev-dependencies]
15 | autorand = { version = "0.2.2", features = ["json", "json-value-always-null", "limited-integers"] }
16 | futures = "0.1.25"
17 | failure = "0.1.5"
18 |
--------------------------------------------------------------------------------
/templates/client/rust/bin/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cargo test --all
4 |
--------------------------------------------------------------------------------
/templates/client/rust/src/lib.rs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-rpc/generator/be8544c89fabdce628144344897b15325b233af4/templates/client/rust/src/lib.rs
--------------------------------------------------------------------------------
/templates/client/rust/src/test_harness.rs:
--------------------------------------------------------------------------------
1 | use jsonrpc_client_core::Transport;
2 | use futures::{Future, future::result};
3 | use failure::{Error, err_msg, Compat};
4 | use std::collections::HashMap;
5 | use serde_json::Value;
6 | use serde::{Serialize, Deserialize};
7 |
8 | pub struct MockTransport {
9 | pub method: String,
10 | pub params: Vec,
11 | pub result: Value,
12 | }
13 |
14 | impl MockTransport {
15 | fn validate(&self, json_data: Vec) -> Result, Error> {
16 | #[derive(Deserialize, Debug)]
17 | struct RequestObject {
18 | method: String,
19 | params: Vec,
20 | id: i64,
21 | }
22 |
23 | #[derive(Serialize, Debug)]
24 | struct Response {
25 | jsonrpc: &'static str,
26 | result: T,
27 | id: i64,
28 | }
29 |
30 | let json: RequestObject = serde_json::from_slice(&json_data)?;
31 |
32 | if json.method != self.method {
33 | return Err(err_msg(
34 | format!("'method' mismatch:\n\texpected {:?}\n\tgot {:?}", self.method, json.method)
35 | ))
36 | }
37 |
38 | if json.params != self.params {
39 | return Err(err_msg(
40 | format!("'params' mispatch:\n\texpected {:?}\n\tgot {:?}", self.params, json.params)
41 | ))
42 | }
43 |
44 | let response = Response {
45 | jsonrpc: "2.0",
46 | result: self.result.clone(),
47 | id: json.id,
48 | };
49 |
50 | Ok(serde_json::to_vec(&response)?)
51 | }
52 | }
53 |
54 | impl Transport for MockTransport {
55 | type Future = Box, Error = Self::Error> + Send>;
56 | type Error = Compat;
57 |
58 | fn get_next_id(&mut self) -> u64 {
59 | 0
60 | }
61 |
62 | fn send(&self, json_data: Vec) -> Self::Future {
63 | Box::new(result(self.validate(json_data).map_err(|e| e.compat())))
64 | }
65 | }
--------------------------------------------------------------------------------
/templates/client/typescript/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "template-client",
3 | "version": "1.0.0",
4 | "description": "",
5 | "type": "module",
6 | "main": "build/index.js",
7 | "scripts": {
8 | "build": "tsc && typedoc src/index.ts --out docs --excludeExternals --excludePrivate --hideGenerator --readme none",
9 | "lint": "eslint src/**/*.ts"
10 | },
11 | "files": [
12 | "build/*"
13 | ],
14 | "author": "",
15 | "license": "Apache-2.0",
16 | "dependencies": {
17 | "@open-rpc/client-js": "1.8.1",
18 | "@open-rpc/meta-schema": "1.14.9",
19 | "@open-rpc/schema-utils-js": "2.1.2",
20 | "lodash": "^4.17.15"
21 | },
22 | "devDependencies": {
23 | "@eslint/js": "9.21.0",
24 | "@typescript-eslint/eslint-plugin": "^8.25.0",
25 | "eslint-config-prettier": "^10.0.1",
26 | "eslint-plugin-prettier": "^5.2.3",
27 | "@types/isomorphic-fetch": "^0.0.39",
28 | "@types/jest": "^29.5.12",
29 | "@types/json-schema": "7.0.3",
30 | "@types/lodash": "^4.14.149",
31 | "@types/ws": "^6.0.1",
32 | "globals": "^16.0.0",
33 | "prettier": "^3.5.2",
34 | "typedoc": "^0.27.9",
35 | "typescript": "^5.0.0"
36 | }
37 | }
--------------------------------------------------------------------------------
/templates/client/typescript/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import eslint from '@eslint/js';
2 | import tseslint from '@typescript-eslint/eslint-plugin';
3 | import tsParser from '@typescript-eslint/parser';
4 | import prettierConfig from 'eslint-config-prettier';
5 | import prettierPlugin from 'eslint-plugin-prettier';
6 | import { fileURLToPath } from 'url';
7 | import { dirname } from 'path';
8 | import globals from "globals";
9 |
10 |
11 | const __filename = fileURLToPath(import.meta.url);
12 | const __dirname = dirname(__filename);
13 |
14 | export default [
15 | eslint.configs.recommended,
16 | {
17 | files: ['**/*.{ts,tsx}'],
18 | languageOptions: {
19 | parser: tsParser,
20 | parserOptions: {
21 | project: ['./tsconfig.json'],
22 | tsconfigRootDir: __dirname,
23 | ecmaVersion: 2022,
24 | sourceType: 'module',
25 | ecmaFeatures: {
26 | }
27 | },
28 | globals: {
29 | ...globals.node,
30 | ...globals.es6,
31 | chrome: 'readonly',
32 | browser: 'readonly'
33 | }
34 | },
35 | plugins: {
36 | '@typescript-eslint': tseslint,
37 | 'prettier': prettierPlugin
38 | },
39 | rules: {
40 | ...tseslint.configs['recommended'].rules,
41 | '@typescript-eslint/explicit-function-return-type': 'off',
42 | '@typescript-eslint/explicit-module-boundary-types': 'off',
43 | '@typescript-eslint/no-explicit-any': 'warn',
44 | '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
45 | 'no-console': ['warn', { allow: ['warn', 'error'] }],
46 | 'prettier/prettier': 'error'
47 | },
48 | ignores: ["package.json","node_modules/", "**/*/dist/", "build/", "coverage/","*.config.js", "**/*.config.ts", "vitest.config.ts", "vite.config.ts"],
49 | settings: {
50 | react: {
51 | version: 'detect'
52 | }
53 | }
54 | },
55 | prettierConfig
56 | ];
--------------------------------------------------------------------------------
/templates/client/typescript/gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 |
--------------------------------------------------------------------------------
/templates/client/typescript/src/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-rpc/generator/be8544c89fabdce628144344897b15325b233af4/templates/client/typescript/src/index.ts
--------------------------------------------------------------------------------
/templates/client/typescript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "lib": [
3 | "ES2015"
4 | ],
5 | "compilerOptions": {
6 | "module": "ESNext",
7 | "esModuleInterop": true,
8 | "target": "ES2022",
9 | "strict": true,
10 | "sourceMap": true,
11 | "outDir": "build",
12 | "declarationDir": "build",
13 | "declaration": true,
14 | "resolveJsonModule": true,
15 | "moduleResolution": "bundler"
16 | },
17 | "typedocOptions": {
18 | "name": "generated client",
19 | "entryPoints": [
20 | "./src/index.ts"
21 | ],
22 | "out": "docs",
23 | "readme": "./README.md",
24 | "exclude": [
25 | "node_modules",
26 | "docs",
27 | "build",
28 | "jest.config.js",
29 | "src/*.test.ts",
30 | "src/**/*.test.ts"
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/templates/docs/gatsby/.node-version:
--------------------------------------------------------------------------------
1 | 20.11.1
--------------------------------------------------------------------------------
/templates/docs/gatsby/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.11.1
--------------------------------------------------------------------------------
/templates/docs/gatsby/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "bracketSameLine": false,
4 | "arrowParens": "always",
5 | "bracketSpacing": true,
6 | "useTabs": false,
7 | "tabWidth": 2,
8 | "printWidth": 100,
9 | "trailingComma": "es5",
10 | "singleQuote": true,
11 | "semi": true
12 | }
13 |
--------------------------------------------------------------------------------
/templates/docs/gatsby/BUILDING.md:
--------------------------------------------------------------------------------
1 | # Building
2 |
3 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14](https://tools.ietf.org/html/bcp14) [RFC2119](https://tools.ietf.org/html/rfc2119) [RFC8174](https://tools.ietf.org/html/rfc8174) when, and only when, they appear in all capitals, as shown here.
4 |
5 | This document is licensed under [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).
6 |
7 | ## Dependencies
8 |
9 | - [graphviz](https://graphviz.gitlab.io/download/)
10 | - [svgbob](https://github.com/ivanceras/svgbob)
11 |
12 |
13 |
14 | ## How to build for production
15 |
16 | `npm run build`
17 |
18 | and you can statically serve the `public/` folder.
19 |
--------------------------------------------------------------------------------
/templates/docs/gatsby/CONVENTIONAL_COMMITS.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-rpc/generator/be8544c89fabdce628144344897b15325b233af4/templates/docs/gatsby/CONVENTIONAL_COMMITS.md
--------------------------------------------------------------------------------
/templates/docs/gatsby/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/templates/docs/gatsby/README.md:
--------------------------------------------------------------------------------
1 |
2 |