@czlonkowski/n8n-nodes-librus 0.1.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/NOTICE.md +7 -0
- package/README.md +152 -0
- package/SECURITY.md +15 -0
- package/dist/credentials/LibrusSessionApi.credentials.d.ts +8 -0
- package/dist/credentials/LibrusSessionApi.credentials.js +32 -0
- package/dist/credentials/LibrusSessionApi.credentials.js.map +1 -0
- package/dist/nodes/Librus/Librus.node.d.ts +10 -0
- package/dist/nodes/Librus/Librus.node.js +225 -0
- package/dist/nodes/Librus/Librus.node.js.map +1 -0
- package/dist/nodes/Librus/LibrusClient.d.ts +67 -0
- package/dist/nodes/Librus/LibrusClient.js +393 -0
- package/dist/nodes/Librus/LibrusClient.js.map +1 -0
- package/dist/nodes/Librus/LibrusTrigger.node.d.ts +10 -0
- package/dist/nodes/Librus/LibrusTrigger.node.js +90 -0
- package/dist/nodes/Librus/LibrusTrigger.node.js.map +1 -0
- package/dist/nodes/Librus/librus.png +0 -0
- package/dist/nodes/Librus/pollState.d.ts +4 -0
- package/dist/nodes/Librus/pollState.js +46 -0
- package/dist/nodes/Librus/pollState.js.map +1 -0
- package/dist/nodes/Librus/transport.d.ts +4 -0
- package/dist/nodes/Librus/transport.js +33 -0
- package/dist/nodes/Librus/transport.js.map +1 -0
- package/docs/architecture.md +53 -0
- package/docs/live-verification.md +23 -0
- package/docs/logo.md +11 -0
- package/docs/verification.md +41 -0
- package/package.json +76 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Architecture and evidence
|
|
2
|
+
|
|
3
|
+
## Execution contract
|
|
4
|
+
|
|
5
|
+
A programmatic node is necessary because login, redirects, cookie handling and paginated message retrieval are dependent requests. `Librus.node.ts` owns n8n parameters, credentials, item pairing and conversion of safe errors. `LibrusClient.ts` owns the session and response validation. `transport.ts` adapts n8n's modern execution helper and legacy-only credential-test helper.
|
|
6
|
+
|
|
7
|
+
No process-global cache, workflow static data, environment secret or file stores authentication state. Each client owns a `tough-cookie` jar. The node never outputs cookies, passwords, raw HTTP errors or login response bodies. Node errors are reconstructed from fixed messages. Downstream output deliberately includes message metadata and optional preview or full content, identified by `contentSource`. Preview remains the default for existing workflows.
|
|
8
|
+
|
|
9
|
+
## Observed community protocol
|
|
10
|
+
|
|
11
|
+
1. GET `https://synergia.librus.pl/loguj/portalRodzina` and follow the browser authorization redirects.
|
|
12
|
+
2. POST form fields `action=login`, `login`, and `pass` to the resulting validated HTTPS `api.librus.pl/OAuth/Authorization` route.
|
|
13
|
+
3. Follow the returned `goTo` authorization continuation. A successful flow may terminate at Synergia `/loguj/portalRodzina`; this callback is allowed, with TokenInfo and inbox access still checked afterward. A terminal verification page stops with `ACTION_REQUIRED`.
|
|
14
|
+
4. Request `/gateway/api/2.0/Auth/TokenInfo/` on Synergia.
|
|
15
|
+
5. Initialize messages through `https://synergia.librus.pl/wiadomosci3`.
|
|
16
|
+
6. GET `https://wiadomosci.librus.pl/api/inbox/messages?page=1&limit=50` and paginate its `data` array. Its `content` field is a truncated preview.
|
|
17
|
+
7. Only when Include Content is enabled and Content Source is Full Message, fetch `/api/inbox/messages/{messageId}` for each selected message in the same session. Validate the returned ID and decode the case-sensitive `data.Message` field as the full body.
|
|
18
|
+
|
|
19
|
+
These are unofficial website routes, not a contracted public API. No verified refresh-token operation is implemented. TokenInfo success is not sufficient proof of inbox access; the credential test also validates a listing response.
|
|
20
|
+
|
|
21
|
+
## Boundaries
|
|
22
|
+
|
|
23
|
+
HTTPS and four exact Librus hostnames are allowlisted. Every redirect is checked before a request is issued. Legacy HTTP Location headers targeting exact allowlisted hosts are upgraded to HTTPS before sending; no request is made over HTTP. URL credentials and non-default ports are rejected. Password-bearing POSTs are restricted to the authorization route and never automatically redirected or replayed. Cookies are obtained from the jar separately for each URL.
|
|
24
|
+
|
|
25
|
+
Requests time out after at most 15 seconds. An operation is bounded to 120 seconds and 100 requests, including a possible second login. Redirect chains stop after 10 redirects. A recognized session expiry during a scan allows one fresh login and restarts the scan, discarding partial results. Generic HTML, malformed JSON and schema mismatches fail rather than returning an empty inbox. A short page is treated as end-of-list; this assumption must be verified live. Pagination is not a transactional snapshot when messages arrive mid-scan.
|
|
26
|
+
|
|
27
|
+
No scheduling or cross-execution deduplication lives in the action node. The separate Librus Trigger owns account-scoped discovery history; downstream workflows own delivery state. Reading listing entries does not explicitly call a mark-read endpoint; side effects still need live measurement.
|
|
28
|
+
|
|
29
|
+
## Source references
|
|
30
|
+
|
|
31
|
+
- [FlakM/librus-rs authentication](https://github.com/FlakM/librus-rs/blob/224bd49c613a2f416f757aab6ad44c40ac2b4d9b/src/lib.rs) and [message schema](https://github.com/FlakM/librus-rs/blob/224bd49c613a2f416f757aab6ad44c40ac2b4d9b/src/structs/messages.rs): browser OAuth and new inbox route.
|
|
32
|
+
- [Mati365/librus-api](https://github.com/Mati365/librus-api/blob/9b47c3c460728fa674e6e6aba530558dbd0b486f/lib/api.js): authorization URL validation and current login sequence. Its legacy inbox is not used here.
|
|
33
|
+
- [n8n community-node development](https://docs.n8n.io/integrations/creating-nodes/overview/): package and node conventions.
|
|
34
|
+
|
|
35
|
+
Implementation was written independently from these protocol observations. Source inspection and offline fixture success are not live integration evidence.
|
|
36
|
+
|
|
37
|
+
## Full message retrieval
|
|
38
|
+
|
|
39
|
+
Complete pagination and deduplication before issuing detail requests. Reject more than 50 selected messages before any detail request. Fetch sequentially within the existing 100-request/120-second operation budget, with one recognized session-expiry recovery for the whole operation. Fail on missing/mismatched details or invalid base64; never silently fall back to preview. No partial output is emitted. Detail requests may affect unread state; previous reads cannot be rolled back on a later failure. The returned readDate remains the pre-detail listing snapshot. Message IDs are restricted to safe alphanumeric, underscore and hyphen path segments before detail retrieval.
|
|
40
|
+
|
|
41
|
+
## Read filters and single-message content
|
|
42
|
+
|
|
43
|
+
Get Many filters listing metadata locally using readDate (null/absent/empty means unread) before applying the requested result limit. Pagination progress is tracked separately from matching results, so a full page of read messages does not prematurely terminate an unread scan. Full bodies are requested only after selection. During session recovery after unread/full selection, retain the selected IDs: re-filtering the inbox could lose messages already marked read by earlier detail requests. Other scan recovery retains the existing restart behaviour. Get Content takes a validated ID and returns only messageId/content/contentSource; it shares the guarded detail route and one-session-recovery limit.
|
|
44
|
+
|
|
45
|
+
## Polling contract
|
|
46
|
+
|
|
47
|
+
LibrusTrigger implements IPollFunctions with polling:true; n8n injects Poll Times. Automatic polls scan the whole inbox across all read statuses. The first complete scan records a silent baseline. Subsequent complete scans emit unseen IDs and retain a union of up to 10000 IDs; reaching capacity or encountering invalid state fails without mutating history. A SHA-256 account fingerprint uses credential ID and username, not password, so password rotation does not replay messages. Changing account establishes a new baseline. Manual mode retrieves a single preview sample and never accesses static data.
|
|
48
|
+
|
|
49
|
+
State comparison and assignment occur after the asynchronous scan, with no await between them. This avoids duplicate emission for overlapping calls sharing the same state object. It does not provide distributed locking for stale copies or multi-instance executions. The cursor advances before downstream delivery; a failed downstream action must be recovered through execution retry, not another poll. IDs of messages moved out of the inbox remain in history. This is discovery deduplication, not a transactional outbox or an exactly-once guarantee.
|
|
50
|
+
|
|
51
|
+
Verified installed n8n 2.37.10 code: n8n-core/dist/nodes-loader/directory-loader.js injects commonPollingParameters; active-workflow-triggers.js runs an activation poll; n8n/dist/workflows/triggers/workflow-trigger-activator.js saves static data after registration. workflow-execution.service.js and poll-cursor.service.js commit a durable cursor with the execution before downstream processing where durable scheduling is enabled. Legacy and durable behaviour depends on host configuration; no host-internal APIs are imported by this package.
|
|
52
|
+
|
|
53
|
+
Public Librus frontend research (2026-09-07): https://wiadomosci.librus.pl/nowy/inbox serves App-DRe0BPBk.js under /nowy/assets/. Its UI includes an unreadOnly filter, a GET inbox detail route and unread counts, but the inspected code exposes no mark-unread action. Only protocol observations were used; no frontend implementation was copied. Local filtering uses the listing contract already exercised by the client rather than relying on another unverified server query parameter.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Live verification checklist
|
|
2
|
+
|
|
3
|
+
Status: **partially verified on 2026-09-07**. A metadata-only execution and an explicit Full Message execution succeeded on one account. The full detail body was longer than the matching preview. Unread-state preservation, broader content-format coverage and pagination remain unverified. Never paste a password or raw message response into a bug report, fixture or terminal command.
|
|
4
|
+
|
|
5
|
+
1. With Node.js 24 active, run `N8N_LISTEN_ADDRESS=127.0.0.1 N8N_PORT=5689 npm run dev`. The isolated profile lives in `../.n8n-librus-dev`; never put it inside this repository. Create credentials inside the n8n UI.
|
|
6
|
+
2. Run the credential test. Record only success or the sanitized error code. Confirm the login works on accounts with and without optional verification prompts; stop when the website requires user action.
|
|
7
|
+
3. Note the IDs and unread status of a small known set in the Librus UI. Execute Get Many with Limit 10 and Include Content off. Compare sender, subject, ID and dates without saving a private fixture.
|
|
8
|
+
4. Reopen the website and verify unread status is unchanged. This must pass before claiming that polling preserves unread state.
|
|
9
|
+
5. Enable Include Content and select Content Source → Full Message for an already-read known message and compare the complete body, Unicode and HTML with the UI. Test a message with an attachment; verify attachment presence only. Confirm whether base64 padding and nullable fields match the strict schema.
|
|
10
|
+
6. Test a second page and end-of-list behaviour. Verify the server honours `limit` and whether short pages always mean the end. Test duplicate IDs and arrival of a new message between scans.
|
|
11
|
+
7. Use controlled synthetic transport tests for expiry and 429; do not deliberately hammer a live account or repeatedly submit wrong passwords.
|
|
12
|
+
8. Before unattended scheduling, measure ordinary session lifetime, login effects on the browser/mobile app, practical polling frequency and concurrent account behaviour. The trigger implements ID deduplication and an initial baseline; verify persistence in the target deployment. Add a workflow error path for authentication failures and incomplete scans.
|
|
13
|
+
|
|
14
|
+
Do not store cookies in workflow static data or node output when adding session reuse. Do not label sessions as OAuth refresh tokens without observing and verifying a supported refresh operation.
|
|
15
|
+
|
|
16
|
+
## New operations and trigger acceptance
|
|
17
|
+
|
|
18
|
+
- Get Many with Read Status Unread must exclude known read messages and apply Limit to matching entries, including across pages. Compare metadata-only results before enabling full-body retrieval.
|
|
19
|
+
- Use Get Content on an already-read ID. Compare its body with Full Message from Get Many without exporting private content.
|
|
20
|
+
- Manually test Librus Trigger: one sample (or none for an empty inbox), no automatic polling or history updates.
|
|
21
|
+
- In a deliberately activated test workflow with no outbound action, confirm the first automatic poll emits no old messages; one newly received message emits once, even if read before polling. Do not send test messages to others without authorization.
|
|
22
|
+
- Restart the test instance with persistent storage; confirm no replay. Exercise downstream-failure recovery with synthetic data and execution retry.
|
|
23
|
+
- Check account-switch baseline, an inbox beyond the page cap, and ordinary session/browser effects before enabling unattended use.
|
package/docs/logo.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Node logo
|
|
2
|
+
|
|
3
|
+
The node and credentials share `nodes/Librus/librus.png`. The transparent PNG combines an open book, envelope and coral unread dot. A midnight teal tile keeps the cream and seafoam mark visible on light and dark n8n canvases. The README uses the same asset.
|
|
4
|
+
|
|
5
|
+
Created with the built-in OpenAI image generation tool on 2026-09-07. This is an independent community mark, not the official LIBRUS logo. No LIBRUS source assets were used.
|
|
6
|
+
|
|
7
|
+
## Generation prompt
|
|
8
|
+
|
|
9
|
+
Use case: logo-brand. Create ONE finished square icon for an open-source n8n node that reads school journal messages, called Librus community integration. Deliver a clean production logo asset, NOT a mockup or presentation sheet. No text, no letters, no watermark. A clever minimal geometric symbol merging an open book and an envelope: two broad creamy-white page shapes form a central V/envelope fold, with a single small warm coral unread-notification dot at the upper right. Place it inside a deep midnight teal rounded-square tile. A small seafoam/teal accent on one page gives depth through flat overlapping shapes only. Outer canvas genuinely transparent, tile fills about 88 percent of square with balanced clear margin. Friendly, precise, polished contemporary open-source software identity. Flat vector-like rendering, crisp curves, generous negative space, no gradients, no 3D, no textures, no drop shadows, no thin lines, at most four solid colors. Excellent legibility at 24 to 60 pixels, attractive on both white and dark gray UI. Original independent symbol; do not reproduce the official LIBRUS logo. Square 1024x1024 output.
|
|
10
|
+
|
|
11
|
+
The returned PNG is 1254 by 1254 pixels with alpha, preserved unchanged. n8n and the README size it at display time.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Bootstrap verification — 2026-09-07
|
|
2
|
+
|
|
3
|
+
- Node.js 24.20.0, TypeScript 5.9.2, @n8n/node-cli 0.46.4, n8n-workflow development types 2.38.1.
|
|
4
|
+
- `npm run check`: clean lint and TypeScript build; 35 synthetic offline tests passed.
|
|
5
|
+
- Tests cover authentication, host-scoped cookies, unsafe redirects, password POST replay prevention, bounded pagination, deduplication, expiry retry, marked login pages, optional fields, base64/UTF-8 validation, safe timeouts and sanitized n8n execution/credential-test errors.
|
|
6
|
+
- Official CLI development mode started n8n 2.37.10 on loopback port 5689 with a separate temporary profile. `/healthz` returned HTTP 200. The generated n8n node and credential registries contained `CUSTOM.librus` and `librusSessionApi`. No user account setup, workflow activation or real Librus login was performed.
|
|
7
|
+
- `npm pack` produced an installable tarball; the manifest entry points, icons and documentation were inspected. Tests, node_modules, development profiles and compiler caches are excluded.
|
|
8
|
+
- One independent GPT-5.6 Sol reader reviewed the client, adapters, credentials and protocol sources. Optional-field, timeout-boundary and login-form classification findings were addressed and covered by regression tests.
|
|
9
|
+
- Runtime audit: zero reported vulnerabilities. Development tooling: ten moderate advisories remain after updating n8n-workflow to remove the initial four high-severity reports; see SECURITY.md.
|
|
10
|
+
|
|
11
|
+
These checks establish package loading and offline behaviour. They do not establish successful live authentication, body completeness, unread-state preservation, pagination semantics or safe unattended polling frequency.
|
|
12
|
+
|
|
13
|
+
## Live login correction — 2026-09-07
|
|
14
|
+
|
|
15
|
+
The user's manual n8n workflow initially returned ACTION_REQUIRED. A diagnostic retry identified the terminal page as the Synergia OAuth callback. The client had incorrectly classified every `/loguj/` path as a login prompt. Allowing the callback exposed a legacy HTTP redirect during the subsequent handoff. Trusted-host HTTP redirects are now upgraded to HTTPS before any request; all other URL checks remain in force.
|
|
16
|
+
|
|
17
|
+
After both fixes, the same workflow succeeded with Limit 1 and Include Content off. The output contained one item with messageId, senderName and readDate fields. Credentials were used through the existing n8n credential entry; no password, cookie, authorization code or message value was copied into this repository or these notes. Browser-session effects, unread-state preservation, content completeness and live pagination were not measured.
|
|
18
|
+
|
|
19
|
+
`npm run check` passed all 40 tests, including successful callback handling, failed post-callback access, retained login challenges, redacted diagnostics and HTTP-to-HTTPS redirect upgrade boundaries.
|
|
20
|
+
|
|
21
|
+
## Full content retrieval — 2026-09-07
|
|
22
|
+
|
|
23
|
+
The listing content was confirmed to be a truncated preview. Added explicit Preview/Full Message selection while retaining Preview as the default. Full Message retrieves the case-sensitive data.Message field from the message detail route, only after listing pagination, limit and deduplication complete. An explicit cap of 50 selected messages bounds extra requests.
|
|
24
|
+
|
|
25
|
+
The existing manual n8n workflow succeeded with Limit 1, Include Content enabled and Content Source set to Full Message. The chosen already-read message matched the user's example. Its preview had 95 characters; its detail body had 463 characters and began with the entire preview. Only lengths and comparison results were recorded; no message contents or identifiers were added to the repository. Unread-state side effects and comparison against the full website rendering remain unverified.
|
|
26
|
+
|
|
27
|
+
`npm run check` passed 55 tests, including backward-compatible preview behaviour, full body decoding, selection before hydration, malformed/mismatched detail responses, no partial/fallback output, safety limits and session recovery during detail retrieval.
|
|
28
|
+
|
|
29
|
+
## Read filters and polling trigger — 2026-09-07
|
|
30
|
+
|
|
31
|
+
Added All/Unread/Read filtering before result limits, Get Content by ID, and a separate registered Librus Trigger. One GPT-5.6 Sol reader traced the installed n8n 2.37.10 polling/runtime persistence contract; the parent verified the decisive activation/save and cursor-before-delivery source paths and implemented the changes.
|
|
32
|
+
|
|
33
|
+
`npm run check` passed 73 synthetic tests. New tests cover nonmatching full pages, filtered limits and hydration, invalid options, single-ID dispatch, unread selection preservation during detail-session recovery, initial silent baseline, serialized restart, read-state changes, account changes, bounded/corrupt state, manual sample isolation, failed scans, shared-state overlapping calls and duplicate risk with independent stale copies. Downstream retry semantics are established from runtime source inspection and the poll cursor contract, not a live failed-notification trial.
|
|
34
|
+
|
|
35
|
+
The development watcher reported successful builds and hot reloads. A manual browser acceptance test could not be completed after the browser automation connection detached; a fresh-tab attempt also timed out. No live unread-filter result, scheduled message-arrival event or restart persistence trial is claimed for these new features. No workflow was activated and no message or notification was sent. Existing n8n credentials were not extracted. Mark-as-unread is not implemented because the inspected public Librus frontend exposed no matching action.
|
|
36
|
+
|
|
37
|
+
## Manual npm publishing — 2026-09-07
|
|
38
|
+
|
|
39
|
+
The unscoped n8n-nodes-librus name belongs to a previous unrelated package. The manifest and lockfile now use @czlonkowski/n8n-nodes-librus at 0.1.0; the GitHub repository keeps its existing name. npm reports no public scoped package entry; authenticated publishing access still needs confirmation.
|
|
40
|
+
|
|
41
|
+
Added scripts/release.mjs and npm run release, with a --dry-run option. The actual dry run completed dependency installation, clean lint/build, all 73 tests, inspection of the 29-file archive and npm publish --dry-run with public access. No version was published. Invalid arguments and dirty-working-tree publication guards were also exercised. The script publishes the inspected tarball and does not create a release pipeline, Git tag or GitHub release. Local npm authentication was absent during verification.
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@czlonkowski/n8n-nodes-librus",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Nieoficjalny węzeł społecznościowy n8n do odczytywania wiadomości z Librus Synergia.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"n8n-community-node-package",
|
|
8
|
+
"librus",
|
|
9
|
+
"synergia"
|
|
10
|
+
],
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Romuald Członkowski",
|
|
13
|
+
"email": "56956555+czlonkowski@users.noreply.github.com"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "n8n-node build",
|
|
17
|
+
"dev": "n8n-node dev --custom-user-folder ../.n8n-librus-dev",
|
|
18
|
+
"lint": "n8n-node lint",
|
|
19
|
+
"lint:fix": "n8n-node lint --fix",
|
|
20
|
+
"format": "prettier --write credentials nodes tests .github package.json tsconfig.json eslint.config.mjs",
|
|
21
|
+
"test": "npm run build && node --test tests/*.test.cjs",
|
|
22
|
+
"check": "npm run lint && npm test",
|
|
23
|
+
"prepublishOnly": "npm run check",
|
|
24
|
+
"prepack": "npm run build",
|
|
25
|
+
"release": "node scripts/release.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/nodes",
|
|
29
|
+
"dist/credentials",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"NOTICE.md",
|
|
32
|
+
"SECURITY.md",
|
|
33
|
+
"CHANGELOG.md",
|
|
34
|
+
"docs"
|
|
35
|
+
],
|
|
36
|
+
"n8n": {
|
|
37
|
+
"n8nNodesApiVersion": 1,
|
|
38
|
+
"strict": false,
|
|
39
|
+
"credentials": [
|
|
40
|
+
"dist/credentials/LibrusSessionApi.credentials.js"
|
|
41
|
+
],
|
|
42
|
+
"nodes": [
|
|
43
|
+
"dist/nodes/Librus/Librus.node.js",
|
|
44
|
+
"dist/nodes/Librus/LibrusTrigger.node.js"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@n8n/node-cli": "0.46.4",
|
|
49
|
+
"@types/node": "24.3.0",
|
|
50
|
+
"eslint": "9.32.0",
|
|
51
|
+
"n8n-workflow": "2.38.1",
|
|
52
|
+
"prettier": "3.6.2",
|
|
53
|
+
"typescript": "5.9.2"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"n8n-workflow": "*"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=24"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"tough-cookie": "6.0.2"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/czlonkowski/n8n-nodes-librus#readme",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "git+https://github.com/czlonkowski/n8n-nodes-librus.git"
|
|
68
|
+
},
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/czlonkowski/n8n-nodes-librus/issues"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public",
|
|
74
|
+
"registry": "https://registry.npmjs.org/"
|
|
75
|
+
}
|
|
76
|
+
}
|