@misiki/gocommerce-connector 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -151
- package/dist/index-BfbB3jQb.d.cts +6452 -0
- package/dist/index-BfbB3jQb.d.ts +6452 -0
- package/dist/index.cjs +2787 -2049
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -1
- package/dist/index.d.ts +85 -1
- package/dist/index.js +2745 -2020
- package/dist/index.js.map +1 -1
- package/dist/services/index.cjs +2740 -2049
- package/dist/services/index.cjs.map +1 -1
- package/dist/services/index.d.cts +1 -4742
- package/dist/services/index.d.ts +1 -4742
- package/dist/services/index.js +2724 -2015
- package/dist/services/index.js.map +1 -1
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -1,187 +1,237 @@
|
|
|
1
1
|
# @misiki/gocommerce-connector
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[](https://www.typescriptlang.org/)
|
|
3
|
+
[Svelte Commerce](https://github.com/itswadesh/svelte-commerce) on a
|
|
4
|
+
[GoCommerce](https://github.com/itswadesh/gocommerce) engine.
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
The storefront talks to every backend through a connector: a package that maps
|
|
7
|
+
one platform's API onto a shared surface of some forty services (`product`,
|
|
8
|
+
`cart`, `checkout`, `order`, `user`, `address`, …). This is GoCommerce's. It
|
|
9
|
+
covers the engine's public API and the storefront modules it ships — accounts,
|
|
10
|
+
reviews, wishlists, content pages, menus, FAQ, contact and search — and says
|
|
11
|
+
plainly where the two do not meet.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
> **Coverage:** 10 of 43 services are wired to the live GoCommerce API — catalogue, search, cart,
|
|
12
|
-
> checkout, payment methods and orders. That is the honest ceiling, not a to-do list: GoCommerce is
|
|
13
|
-
> guest-checkout only by design, so there are no accounts, wishlists, reviews, coupons or CMS to
|
|
14
|
-
> wire. Those services answer with empty lists, and their writes throw a named message rather than
|
|
15
|
-
> succeeding silently.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🚀 Step-by-Step Integration Guide
|
|
20
|
-
|
|
21
|
-
### 1. Install the Connector
|
|
22
|
-
|
|
23
|
-
Inside your `svelte-commerce` project directory, run:
|
|
13
|
+
## Install
|
|
24
14
|
|
|
25
15
|
```bash
|
|
26
|
-
bun
|
|
16
|
+
bun remove @misiki/litekart-connector @misiki/vendure-connector
|
|
17
|
+
bun add @misiki/gocommerce-connector
|
|
27
18
|
```
|
|
28
19
|
|
|
29
|
-
|
|
20
|
+
The storefront runs on whichever `@misiki/*-connector` is installed. If you keep
|
|
21
|
+
another one installed, set `PUBLIC_CONNECTOR=@misiki/gocommerce-connector`.
|
|
22
|
+
|
|
30
23
|
```bash
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
|
|
24
|
+
# .env
|
|
25
|
+
PUBLIC_GOCOMMERCE_API_URL=https://api.shop.example # the engine
|
|
26
|
+
PUBLIC_GOCOMMERCE_PROXY_PATH=/gocommerce # see "The browser and CORS"
|
|
27
|
+
# PUBLIC_GOCOMMERCE_CHANNEL=web # a sales channel, if the store has several
|
|
28
|
+
# PUBLIC_GOCOMMERCE_STORE=acme # under `gocommerce platform`, on a shared API host
|
|
29
|
+
# PUBLIC_GOCOMMERCE_LANGUAGE=fr # ask for this language (?lang=) on every read
|
|
34
30
|
```
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
**Leave `PUBLIC_LITEKART_API_URL` unset.** The storefront rewrites every
|
|
33
|
+
server-side request whose path starts with `/api` to that host, and the
|
|
34
|
+
engine's routes start with `/api` too.
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
override module:
|
|
36
|
+
### The engine
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
// kitcommerce.config.ts
|
|
43
|
-
export * as services from './src/lib/core/connectors/gocommerce'
|
|
38
|
+
Start it with the modules the storefront has screens for:
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
40
|
+
```bash
|
|
41
|
+
gocommerce serve -identity -wishlist -reviews -cms -faq -menus
|
|
48
42
|
```
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
Each is optional. Without `-identity` there is no sign-in (guest checkout
|
|
45
|
+
always works); without `-reviews` products show no stars; and so on. Add
|
|
46
|
+
`-meilisearch` (with `MEILI_HOST` and keys) for price sorting and price filters,
|
|
47
|
+
`-newsletter` and `-contact` for the signup box and contact form, and `-b2b` for
|
|
48
|
+
business buyers (see "Beyond the storefront's screens").
|
|
49
|
+
|
|
50
|
+
### The browser and CORS
|
|
51
|
+
|
|
52
|
+
The engine sends no CORS headers, so a browser on the storefront's origin cannot
|
|
53
|
+
read a response from the engine's. Server-side rendering is unaffected; it is
|
|
54
|
+
the bag, sign-in and checkout — which run in the browser — that need a route.
|
|
55
|
+
|
|
56
|
+
Proxy a path on the storefront's own origin to the engine and name it in
|
|
57
|
+
`PUBLIC_GOCOMMERCE_PROXY_PATH`. The browser then calls
|
|
58
|
+
`https://shop.example/gocommerce/api/carts`, and the server keeps calling
|
|
59
|
+
`PUBLIC_GOCOMMERCE_API_URL` directly. With Caddy:
|
|
60
|
+
|
|
61
|
+
```caddyfile
|
|
62
|
+
shop.example {
|
|
63
|
+
handle_path /gocommerce/* {
|
|
64
|
+
reverse_proxy engine:8080
|
|
65
|
+
}
|
|
66
|
+
reverse_proxy storefront:3000
|
|
67
|
+
}
|
|
56
68
|
```
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
directly:
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { BaseService, productService } from '@misiki/gocommerce-connector'
|
|
70
|
+
or nginx:
|
|
63
71
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
```nginx
|
|
73
|
+
location /gocommerce/ { proxy_pass http://engine:8080/; }
|
|
74
|
+
location / { proxy_pass http://storefront:3000; }
|
|
67
75
|
```
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
Serving the storefront and the engine from one origin works too; then leave the
|
|
78
|
+
proxy path unset and point the API URL at that origin.
|
|
79
|
+
|
|
80
|
+
## What maps to what
|
|
81
|
+
|
|
82
|
+
| Storefront | Engine |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| product page, listings, search | `/api/products`, `/api/products/slug/{slug}` (an id or SKU also works) |
|
|
85
|
+
| category pages and tree | `/api/categories`, `/api/categories/{slug}` — a category sells its descendants |
|
|
86
|
+
| collections | `/api/collections`, `/api/collections/{slug}` |
|
|
87
|
+
| featured / trending shelves | the collection with slug `featured` / `trending`; newest products when there is none |
|
|
88
|
+
| bag | `/api/carts…`, `/api/carts/{token}/discount` |
|
|
89
|
+
| delivery options | `/api/checkout/rates` |
|
|
90
|
+
| payment methods | `/api/checkout` — cash on delivery, Razorpay, and the first hosted-page gateway (see below) |
|
|
91
|
+
| place order | `POST /api/checkout/{code}` with an `Idempotency-Key` |
|
|
92
|
+
| order lookup | `/api/orders/{number}?token=` (guest), `/x/identity/me/orders` (account) |
|
|
93
|
+
| sign-in, sign-up, profile, password reset | `/x/identity/*` |
|
|
94
|
+
| address book | `/x/identity/me/addresses` |
|
|
95
|
+
| wishlist | `/x/wishlist` |
|
|
96
|
+
| reviews | `/x/reviews` |
|
|
97
|
+
| content pages (`/p/…`, legal pages) | `/x/cms/pages` |
|
|
98
|
+
| FAQ | `/x/faq` |
|
|
99
|
+
| menus | `/x/navigation/menus/{header,footer}`, laid over `store.menu`; any menu by handle |
|
|
100
|
+
| product specs table | the product's category attribute answers |
|
|
101
|
+
| attribute filters (`?attributes.color=Red`) | `/api/products?attr=color:Red` |
|
|
102
|
+
| newsletter box | `/x/newsletter/subscribe` |
|
|
103
|
+
| contact form, product enquiries | `/x/contact/messages` |
|
|
104
|
+
| price sort and filter, type-ahead | `/x/meilisearch/search`, falling back to the engine's own search |
|
|
105
|
+
| store record | the storefront's own config, with the engine's currency, menus and module switches over it |
|
|
106
|
+
|
|
107
|
+
Blogs, banners, reels, chat, warranties, deals and uploads have no engine
|
|
108
|
+
equivalent. Their lists answer empty and anything that would write or open one
|
|
109
|
+
record throws `NotSupportedError`, naming the service and the method — a silent
|
|
110
|
+
success would tell a shopper their message was sent when nothing left the
|
|
111
|
+
browser.
|
|
112
|
+
|
|
113
|
+
## Beyond the storefront's screens
|
|
114
|
+
|
|
115
|
+
Every shopper-facing route the engine serves is reachable through this package —
|
|
116
|
+
CI fails when the engine gains one that is not (`scripts/route-coverage.mjs`). The
|
|
117
|
+
routes Svelte Commerce has no screen for are reached through services named for
|
|
118
|
+
what they do. These answer the engine's own shapes — numeric ids, money as
|
|
119
|
+
`{ amount_minor, currency }` — where the family's services answer Litekart's.
|
|
120
|
+
|
|
121
|
+
| Service | Covers |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `b2bService` (ext/b2b) | the company, roles and credit (`me`); members and roles; invitations; a quick order by SKU (`addLines`) and a repeat order (`reorder`); checkout on account with a PO number and partial lines, or an approval request; approve, reject, withdraw; quotes requested, read, accepted, declined; the public dealer form (`submitLead`) and a dealer's leads |
|
|
124
|
+
| `checkoutService.checkout(code)` | any payment method the engine has switched on, answering a client secret, a redirect URL or nothing to do; `listMethods()` names them all |
|
|
125
|
+
| `variantService` | a product's variants, one variant, and the marketplace's offers on it |
|
|
126
|
+
| `newsletterService` | subscribe and unsubscribe |
|
|
127
|
+
| `userService` | `requestEmailVerification()`, `refreshSession()`, and `claimOrder({ number })` — files a guest order under the account, explicitly: never at sign-in, where a shared computer would file one person's orders under the next |
|
|
128
|
+
| `menuService` | `listMenus()` and `getMenu(handle)` beyond the header and footer |
|
|
129
|
+
| `wishlistService` | `setEmail()` for back-in-stock mail |
|
|
130
|
+
|
|
131
|
+
The admin API is deliberately not here. It takes an operator's token, and a
|
|
132
|
+
storefront package runs in every shopper's browser.
|
|
74
133
|
|
|
75
134
|
```ts
|
|
76
|
-
import {
|
|
135
|
+
import { b2bService } from '@misiki/gocommerce-connector'
|
|
77
136
|
|
|
78
|
-
|
|
137
|
+
const { role, credit } = await b2bService.me()
|
|
138
|
+
const { cart, rejected } = await b2bService.addLines({ lines: [{ sku: 'TEE-M', quantity: 24 }] })
|
|
139
|
+
const placed = await b2bService.checkout({ poNumber: 'PO-1042' })
|
|
140
|
+
if (placed.kind === 'approval') console.log('waiting for an approver', placed.approval.id)
|
|
79
141
|
```
|
|
80
142
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
143
|
+
## Two things this package is careful about
|
|
144
|
+
|
|
145
|
+
**Money.** The engine speaks minor units and a currency code and never a
|
|
146
|
+
formatted string. The storefront wants `price: 19.99`. The conversion happens in
|
|
147
|
+
`money.ts` and nowhere else, and it is per currency: the yen has no decimals and
|
|
148
|
+
the dinar has three, so ¥2,500 stays 2500 rather than becoming 25.
|
|
149
|
+
|
|
150
|
+
**Nothing is invented.** There is no popularity score, so "most popular" sorts
|
|
151
|
+
nothing rather than sorting by a made-up number. No compare-at price means `mrp`
|
|
152
|
+
equals the price, not 0 — which would render as a 100% discount. Counts and page
|
|
153
|
+
totals are the engine's own.
|
|
154
|
+
|
|
155
|
+
## Where the two do not meet yet
|
|
156
|
+
|
|
157
|
+
Each of these is a real gap, listed so nobody has to find it in production.
|
|
158
|
+
|
|
159
|
+
- **The order-success page shows no details.** The storefront looks the order up
|
|
160
|
+
on the server with only the number and cart id from the URL. A guest's order is
|
|
161
|
+
readable only with the access token checkout returned, which lives in the
|
|
162
|
+
browser that placed it, so the server answers "not available yet". The bag
|
|
163
|
+
still empties, and the order is readable from that browser and, for a
|
|
164
|
+
signed-in shopper, under My orders. Using the cart token in the URL as a
|
|
165
|
+
credential would have fixed the page and made that URL — which lands in
|
|
166
|
+
history and referrers — a key to the shopper's address.
|
|
167
|
+
- **A missing product is a 503, not a 404.** The storefront decides by the status
|
|
168
|
+
of a request to exactly `/api/products/<slug>`; the engine's slug route is
|
|
169
|
+
`/api/products/slug/<slug>`, and `/api/products/<slug>` answers 400 for a
|
|
170
|
+
non-numeric id.
|
|
171
|
+
- **Payment.** The storefront drives a fixed list of gateway flows and passes
|
|
172
|
+
none of them the gateway the shopper chose. Cash on delivery and Razorpay's
|
|
173
|
+
modal match engine gateways directly. The storefront's redirect flow (which it
|
|
174
|
+
reaches by the code `PAYPAL`) carries the store's first hosted-page gateway —
|
|
175
|
+
Adyen, Paddle, Lemon Squeezy, Creem, Hyperswitch or RevenueCat — under that
|
|
176
|
+
gateway's own name and a neutral card icon; a second one cannot be told apart
|
|
177
|
+
from the first and is not offered. GoCommerce's Stripe is a Payment Element (a
|
|
178
|
+
client secret), which this storefront does not mount. Every method left off is
|
|
179
|
+
reported once in the console, and every one is reachable through
|
|
180
|
+
`checkoutService.checkout(code)` for a storefront that drives its own payment
|
|
181
|
+
UI. Payment is confirmed by the gateway's webhook to the engine; the order
|
|
182
|
+
shows `paymentStatus` once it lands.
|
|
183
|
+
- **Attribute filters inside a category.** The engine filters by attribute on
|
|
184
|
+
the whole-shop listing only; on a category page they are not applied.
|
|
185
|
+
- **Tax in the bag.** The engine's cart carries no tax or total; tax is computed
|
|
186
|
+
when the order is placed. The bag shows subtotal − discount + delivery, and the
|
|
187
|
+
order shows the real figures.
|
|
188
|
+
- **Facets.** The engine's search returns hits and a total, not distributions, so
|
|
189
|
+
category and tag facets and the price slider are not shown.
|
|
190
|
+
- **Phone sign-in by code, account deletion, guest order lookup by email and
|
|
191
|
+
code** are not engine features and throw.
|
|
192
|
+
|
|
193
|
+
## State kept in the browser
|
|
194
|
+
|
|
195
|
+
The engine holds a cart's lines and email; the address, phone, delivery choice
|
|
196
|
+
and coupon are sent with the checkout request, so they are kept in the
|
|
197
|
+
shopper's `localStorage` until then. So is the account session token (the
|
|
198
|
+
engine's identity module is bearer-only), each order's access token, and the
|
|
199
|
+
wishlist token. Nothing about a shopper is ever held in module scope: the
|
|
200
|
+
storefront renders some services from a server singleton shared by every
|
|
201
|
+
request.
|
|
98
202
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
GoCommerce sends money as an integer count of the currency's minor unit plus its code
|
|
102
|
-
(`{ amount_minor: 2500, currency: "USD" }`). `fromMoney` converts once, against the currency the
|
|
103
|
-
amount arrived with, into the plain major-unit number the storefront's components print.
|
|
104
|
-
|
|
105
|
-
### The cart
|
|
106
|
-
|
|
107
|
-
A cart is an opaque token and possessing it is the whole authorisation. The connector persists it as
|
|
108
|
-
`cart_id`. A write that fails is followed by a re-read: if the cart is gone or no longer `open`, the
|
|
109
|
-
token is dropped and the item goes into a fresh cart; if the cart is alive, the failure was real and
|
|
110
|
-
is passed straight through.
|
|
111
|
-
|
|
112
|
-
### The address step, sessions and the address book
|
|
113
|
-
|
|
114
|
-
GoCommerce has no address on a cart — the delivery address is an argument to
|
|
115
|
-
`POST /api/checkout/{code}`. The connector holds the shopper's details in the browser against the
|
|
116
|
-
cart id (`checkout-details`) and spends them when the order is placed. Saved addresses are likewise
|
|
117
|
-
browser-local (`address-book`); they never leave the device except as part of that call.
|
|
118
|
-
|
|
119
|
-
Because there are no accounts, a `connect.sid` / `me` cookie can only be left over from another
|
|
120
|
-
backend. `hasPhantomSession` / `clearPhantomSession` let the storefront drop them at load.
|
|
121
|
-
|
|
122
|
-
### Checkout, payment and orders
|
|
123
|
-
|
|
124
|
-
`GET /api/checkout` lists the installed payment codes; `POST /api/checkout/{code}` validates the
|
|
125
|
-
cart, reserves inventory and creates the order in one transaction, with an `Idempotency-Key`.
|
|
126
|
-
Gateway fields ride on `payment.client_data` and are spread onto the return value. Only `cod` is
|
|
127
|
-
verified end to end. The order's `access_token`, returned once, is stored so "my orders" is every
|
|
128
|
-
order placed from this browser.
|
|
129
|
-
|
|
130
|
-
### Catalogue and search
|
|
131
|
-
|
|
132
|
-
The shopper-facing catalogue takes `q`, `limit`, `page` and a category or collection slug — no sort,
|
|
133
|
-
no tag filter, no price range. Sorting and facets are applied in the connector over one window of
|
|
134
|
-
200 products, the API's own `limit` ceiling. Product images are read from both `image_url` and each
|
|
135
|
-
variant's `image`, because the former is missing from the OpenAPI document while the server returns
|
|
136
|
-
it on every read.
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Service behaviour
|
|
141
|
-
|
|
142
|
-
| Service | Behaviour |
|
|
143
|
-
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
144
|
-
| `ProductService`, `SearchService` | `/api/products`, `/api/products/slug/{slug}`, `/api/categories/{slug}`; sort and facets local |
|
|
145
|
-
| `CategoryService`, `CollectionService` | `/api/categories`, `/api/collections` and their slug lookups |
|
|
146
|
-
| `MeilisearchService`, `AutocompleteService` | `?q=` on the catalogue — GoCommerce ships no separate search engine |
|
|
147
|
-
| `CartService` | `/api/carts` and its line-items; the address step is held in the browser |
|
|
148
|
-
| `CheckoutService`, `PaymentMethodService` | `GET /api/checkout`, `POST /api/checkout/{code}` |
|
|
149
|
-
| `OrderService` | `/api/orders/{number}?token=…`; the confirmation page falls back to the converted cart |
|
|
150
|
-
| `StoreService`, `MenuService`, `CurrencyService`, `PluginService`, `SettingService`, `RegionService`, … | The registered static store |
|
|
151
|
-
| `AuthService`, `UserService`, `ProfileService` | No accounts exist — reads answer empty, writes explain why |
|
|
152
|
-
| `AddressService` | A browser-local address book |
|
|
153
|
-
| `PageService`, `BlogService`, `BannerService`, `GalleryService`, `ReelsService` | Empty — no CMS |
|
|
154
|
-
| `WishlistService`, `CouponService`, `ReviewService`, `VendorService` | Empty lists; writes throw a named message |
|
|
155
|
-
| `ContactService`, `EnquiryService`, `UploadService` | Throw — there is no inbox behind them |
|
|
203
|
+
## Development
|
|
156
204
|
|
|
157
|
-
|
|
205
|
+
```bash
|
|
206
|
+
npm install
|
|
207
|
+
npm run typecheck
|
|
208
|
+
npm test # builds, then runs both suites
|
|
209
|
+
```
|
|
158
210
|
|
|
159
|
-
|
|
211
|
+
`test/unit.test.mjs` covers the translation with no engine. The live suites,
|
|
212
|
+
`test/live.test.mjs` and `test/live-more.test.mjs`, run the connector against a
|
|
213
|
+
real one, seeding what they need through the admin API under names unique to
|
|
214
|
+
the run:
|
|
160
215
|
|
|
161
216
|
```bash
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
bun p # build and publish
|
|
217
|
+
gocommerce serve -admin-token dev-token -identity -wishlist -reviews -cms -faq -menus -newsletter -contact -b2b
|
|
218
|
+
GOCOMMERCE_URL=http://127.0.0.1:8080 GOCOMMERCE_ADMIN_TOKEN=dev-token \
|
|
219
|
+
GOCOMMERCE_TEST_DB=postgres://…/the-stores-database npm test
|
|
166
220
|
```
|
|
167
221
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
| `orders.ts` | guest order lookup by access token |
|
|
182
|
-
| `store.ts` | store identity from the registered static config |
|
|
183
|
-
| `static-store.ts` | the static store provider and the feature toggles forced off |
|
|
184
|
-
| `unsupported.ts` | what GoCommerce deliberately does not have |
|
|
222
|
+
`GOCOMMERCE_TEST_DB` lets the b2b suite mark its test accounts' emails
|
|
223
|
+
confirmed — the engine confirms one only from a link it mails — and without it
|
|
224
|
+
those tests skip. Use a throwaway store. The suites skip, loudly, when there is
|
|
225
|
+
no engine. CI runs them against GoCommerce's `main` on every push and nightly,
|
|
226
|
+
along with the route-coverage check.
|
|
227
|
+
|
|
228
|
+
## Releasing
|
|
229
|
+
|
|
230
|
+
Bump `version` in `package.json` and push it to `main`. The Publish workflow
|
|
231
|
+
sees a version npm does not have yet, builds, tests, publishes it with
|
|
232
|
+
provenance, and creates the matching `v<version>` tag and GitHub release. It
|
|
233
|
+
needs an `NPM_TOKEN` secret in the repository's `npm-publish` environment, able
|
|
234
|
+
to publish under `@misiki`.
|
|
185
235
|
|
|
186
236
|
## License
|
|
187
237
|
|