Initial commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
packs-src/
|
||||
packs/
|
||||
*.zip
|
||||
.DS_Store
|
||||
.env
|
||||
@@ -0,0 +1,41 @@
|
||||
# Changelog — hbm-rpg-v3-lore
|
||||
|
||||
All notable changes to the **HbM RPG v3 — Lore** Foundry VTT module.
|
||||
|
||||
## [1.1.0] — 2026-04-30
|
||||
|
||||
Version bump to track the companion system release. No content changes.
|
||||
|
||||
## [1.0.0] — 2026-04-29
|
||||
|
||||
First production release.
|
||||
|
||||
### Added
|
||||
|
||||
- 7 `JournalEntry` compendia generated from `_books/*.md` (36 entries total):
|
||||
- `core-rules-lore` — Podręcznik Gry (5)
|
||||
- `magic-book-lore` — Księga Magii (7)
|
||||
- `bestiary-lore` — Bestiariusz (1)
|
||||
- `blood-magic-history` — Arcanum Sanguinis (4)
|
||||
- `crimson-cult` — Chwała Szkarłatnemu Kultowi (5)
|
||||
- `abyss` — Klątwa Otchłani (5)
|
||||
- `economy` — Złoto, Stal i Magia (9)
|
||||
- Build pipeline: `scripts/parsers/journal-parser.ts` walks
|
||||
`Rozdział [IVX]+ [:.\-] Title`, deduplicates TOC vs. body, splits chapters by
|
||||
`____` separators into pages, converts markdown → HTML
|
||||
(paragraphs / lists / headings / bold / italic / `[[wikilink]]` →
|
||||
`<a class="lore-link">` for downstream UUID rewriting).
|
||||
- `scripts/build-journal-packs.ts` writes per-pack JSON and compiles to LevelDB.
|
||||
- `scripts/package.ts` produces `hbm-rpg-v3-lore-vX.Y.Z.zip`.
|
||||
- `scripts/lint-uuid-refs.ts` framework for cross-link validation.
|
||||
- Declares `relationships.systems[hbm-rpg-v3 ≥1.0.0]`.
|
||||
|
||||
### Deferred
|
||||
|
||||
- Przewodnik Ludzkości po Magicznym Świecie packs (`world-overview`, `deities`,
|
||||
`universe`, `sol3-society`, `afterlife`, `documents`) — book not yet in
|
||||
`_books/`. Will land in v1.1.0.
|
||||
- Automatic UUID cross-link injection (parser emits `lore-link` placeholders
|
||||
ready for a follow-up rewrite pass).
|
||||
- Reverse cross-links from system NPC actors to lore journal pages.
|
||||
- Asset bundling from vault `_assets/`.
|
||||
@@ -0,0 +1,25 @@
|
||||
# HbM RPG v3 — Lore (Foundry VTT module)
|
||||
|
||||
Companion lore module for the [HbM RPG v3 system](https://github.com/iwo-strzebonski/hbm-foundry-system). Ships JournalEntry compendia for setting, history, and narrative chapters from the official books.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run build:packs # parse _books/*.md → packs/ (LevelDB)
|
||||
bun run package # zip into hbm-rpg-v3-lore-vX.Y.Z.zip
|
||||
```
|
||||
|
||||
Requires the `_books/` folder at the repo root (synced via the parent CLI).
|
||||
|
||||
## Packs
|
||||
|
||||
| Pack | Source book |
|
||||
|------|-------------|
|
||||
| `core-rules-lore` | Podręcznik Gry |
|
||||
| `magic-book-lore` | Księga Magii |
|
||||
| `bestiary-lore` | Bestiariusz |
|
||||
| `blood-magic-history` | Arcanum Sanguinis |
|
||||
| `crimson-cult` | Chwała Szkarłatnemu Kultowi |
|
||||
| `abyss` | Klątwa Otchłani |
|
||||
| `economy` | Złoto, Stal i Magia |
|
||||
@@ -0,0 +1,313 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "hbm-rpg-v3-lore",
|
||||
"devDependencies": {
|
||||
"@foundryvtt/foundryvtt-cli": "^3.0.3",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"archiver": "^7.0.1",
|
||||
"typescript": "^5.5.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@foundryvtt/foundryvtt-cli": ["@foundryvtt/foundryvtt-cli@3.0.3", "", { "dependencies": { "chalk": "^5.4.1", "classic-level": "^1.4.1", "esm": "^3.2.25", "js-yaml": "^4.1.0", "mkdirp": "^3.0.1", "nedb-promises": "^6.2.3", "yargs": "^17.7.2" }, "bin": { "fvtt": "fvtt.mjs" } }, "sha512-byNLOrZ9ev6PfW+gflhonT5Y+Goq2aHERwVGWO3eYRKez+lzZJoqMY/YMEi54XYozzWWn8d6LuRbBsVPJ30haw=="],
|
||||
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@seald-io/binary-search-tree": ["@seald-io/binary-search-tree@1.0.3", "", {}, "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA=="],
|
||||
|
||||
"@seald-io/nedb": ["@seald-io/nedb@4.1.2", "", { "dependencies": { "@seald-io/binary-search-tree": "^1.0.3", "localforage": "^1.10.0", "util": "^0.12.5" } }, "sha512-bDr6TqjBVS2rDyYM9CPxAnotj5FuNL9NF8o7h7YyFXM7yruqT4ddr+PkSb2mJvvw991bqdftazkEo38gykvaww=="],
|
||||
|
||||
"@types/archiver": ["@types/archiver@7.0.0", "", { "dependencies": { "@types/readdir-glob": "*" } }, "sha512-/3vwGwx9n+mCQdYZ2IKGGHEFL30I96UgBlk8EtRDDFQ9uxM1l4O5Ci6r00EMAkiDaTqD9DQ6nVrWRICnBPtzzg=="],
|
||||
|
||||
"@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
|
||||
|
||||
"@types/readdir-glob": ["@types/readdir-glob@1.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
|
||||
"abstract-level": ["abstract-level@1.0.4", "", { "dependencies": { "buffer": "^6.0.3", "catering": "^2.1.0", "is-buffer": "^2.0.5", "level-supports": "^4.0.0", "level-transcoder": "^1.0.1", "module-error": "^1.0.1", "queue-microtask": "^1.2.3" } }, "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", "buffer-crc32": "^1.0.0", "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", "zip-stream": "^6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="],
|
||||
|
||||
"archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
|
||||
"b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="],
|
||||
|
||||
"bare-fs": ["bare-fs@4.7.1", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw=="],
|
||||
|
||||
"bare-os": ["bare-os@3.9.0", "", {}, "sha512-JTjuZyNIDpw+GytMO4a6TK1VXdVKKJr6DRxEHasyuYyShV2deuiHJK/ahGZlebc+SG0/wJCB9XK8gprBGDFi/Q=="],
|
||||
|
||||
"bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="],
|
||||
|
||||
"bare-stream": ["bare-stream@2.13.1", "", { "dependencies": { "streamx": "^2.25.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-abort-controller": "*", "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-abort-controller", "bare-buffer", "bare-events"] }, "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow=="],
|
||||
|
||||
"bare-url": ["bare-url@2.4.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-/9a2j4ac6ckpmAHvod/ob7x439OAHst/drc2Clnq+reRYd/ovddwcF4LfoxHyNk5AuGBnPg+HqFjmE/Zpq6v0A=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
|
||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
||||
|
||||
"call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"catering": ["catering@2.1.1", "", {}, "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w=="],
|
||||
|
||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"classic-level": ["classic-level@1.4.1", "", { "dependencies": { "abstract-level": "^1.0.2", "catering": "^2.1.0", "module-error": "^1.0.1", "napi-macros": "^2.2.2", "node-gyp-build": "^4.3.0" } }, "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
|
||||
|
||||
"crc32-stream": ["crc32-stream@6.0.0", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" } }, "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"esm": ["esm@3.2.25", "", {}, "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA=="],
|
||||
|
||||
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||
|
||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||
|
||||
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
|
||||
|
||||
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||
|
||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="],
|
||||
|
||||
"is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
|
||||
|
||||
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="],
|
||||
|
||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||
|
||||
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
|
||||
|
||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="],
|
||||
|
||||
"level-supports": ["level-supports@4.0.1", "", {}, "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA=="],
|
||||
|
||||
"level-transcoder": ["level-transcoder@1.0.1", "", { "dependencies": { "buffer": "^6.0.3", "module-error": "^1.0.1" } }, "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w=="],
|
||||
|
||||
"lie": ["lie@3.1.1", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw=="],
|
||||
|
||||
"localforage": ["localforage@1.10.0", "", { "dependencies": { "lie": "3.1.1" } }, "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg=="],
|
||||
|
||||
"lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
|
||||
|
||||
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
|
||||
|
||||
"module-error": ["module-error@1.0.2", "", {}, "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA=="],
|
||||
|
||||
"napi-macros": ["napi-macros@2.2.2", "", {}, "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g=="],
|
||||
|
||||
"nedb-promises": ["nedb-promises@6.2.3", "", { "dependencies": { "@seald-io/nedb": "^4.0.2" } }, "sha512-enq0IjNyBz9Qy9W/QPCcLGh/QORGBjXbIeZeWvIjO3OMLyAvlKT3hiJubP2BKEiFniUlR3L01o18ktqgn5jxqA=="],
|
||||
|
||||
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||
|
||||
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
||||
|
||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
||||
|
||||
"readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"streamx": ["streamx@2.25.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"tar-stream": ["tar-stream@3.1.8", "", { "dependencies": { "b4a": "^1.6.4", "bare-fs": "^4.5.5", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ=="],
|
||||
|
||||
"teex": ["teex@1.0.1", "", { "dependencies": { "streamx": "^2.12.5" } }, "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg=="],
|
||||
|
||||
"text-decoder": ["text-decoder@1.2.7", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
|
||||
|
||||
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
|
||||
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"HBM_LORE": {
|
||||
"module": { "name": "Homebrew Magic: RPG v3 — Lore" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"HBM_LORE": {
|
||||
"module": { "name": "Homebrew Magic: RPG v3 — Lore" }
|
||||
}
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
{
|
||||
"id": "hbm-rpg-v3-lore",
|
||||
"title": "Homebrew Magic: RPG v3 — Lore",
|
||||
"description": "Companion lore module for the HbM RPG v3 system. Provides journal entries for setting, deities, history, and narrative chapters from the official books.",
|
||||
"version": "1.3.4",
|
||||
"manifest": "https://vtt-content.octoturge.com/packages/module/hbm-rpg-v3-lore.json",
|
||||
"download": "https://vtt-content.octoturge.com/packages/module/hbm-rpg-v3-lore-v1.3.4.zip",
|
||||
"compatibility": {
|
||||
"minimum": "13",
|
||||
"verified": "14"
|
||||
},
|
||||
"relationships": {
|
||||
"systems": [
|
||||
{
|
||||
"id": "hbm-rpg-v3",
|
||||
"type": "system",
|
||||
"compatibility": {
|
||||
"minimum": "1.0.0",
|
||||
"verified": "1.0.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Iwo Strzeboński"
|
||||
}
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "pl",
|
||||
"name": "Polski",
|
||||
"path": "lang/pl.json"
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"name": "English",
|
||||
"path": "lang/en.json"
|
||||
}
|
||||
],
|
||||
"packs": [
|
||||
{
|
||||
"name": "abyss",
|
||||
"label": "Otchłań",
|
||||
"path": "packs/abyss",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "abyss-curse"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "crimson-cult",
|
||||
"label": "Szkarłatny Kult",
|
||||
"path": "packs/crimson-cult",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "crimson-cult"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "blood-magic-history",
|
||||
"label": "Magia Krwi — Historia",
|
||||
"path": "packs/blood-magic-history",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "arcanum-sanguinis"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "economy",
|
||||
"label": "Złoto, Stal i Magia",
|
||||
"path": "packs/economy",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "gold-steel-magic"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "magic-book-lore",
|
||||
"label": "Księga Magii — Wprowadzenia",
|
||||
"path": "packs/magic-book-lore",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "magic-book"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "core-rules-lore",
|
||||
"label": "Podręcznik Gry — Wprowadzenia",
|
||||
"path": "packs/core-rules-lore",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "core-rules"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bestiary-lore",
|
||||
"label": "Bestiariusz — Lore",
|
||||
"path": "packs/bestiary-lore",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "bestiary"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "humanity-guide",
|
||||
"label": "Przewodnik Ludzkości po Magicznym Świecie",
|
||||
"path": "packs/humanity-guide",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "lore",
|
||||
"sourceBook": "humanity-guide"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "disciplines-lore",
|
||||
"label": "Dziedziny Magii",
|
||||
"path": "packs/disciplines-lore",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "organizations",
|
||||
"label": "Organizacje",
|
||||
"path": "packs/organizations",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "races-lore",
|
||||
"label": "Rasy",
|
||||
"path": "packs/races-lore",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "classes",
|
||||
"label": "Klasy (Grupy)",
|
||||
"path": "packs/classes",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "concepts",
|
||||
"label": "Koncepty",
|
||||
"path": "packs/concepts",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "locations",
|
||||
"label": "Lokacje",
|
||||
"path": "packs/locations",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "npcs",
|
||||
"label": "Postacie",
|
||||
"path": "packs/npcs",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conflicts",
|
||||
"label": "Konflikty",
|
||||
"path": "packs/conflicts",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "player-characters",
|
||||
"label": "Postacie Graczy",
|
||||
"path": "packs/player-characters",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "adventures",
|
||||
"label": "Przygody",
|
||||
"path": "packs/adventures",
|
||||
"type": "JournalEntry",
|
||||
"ownership": {
|
||||
"PLAYER": "OBSERVER",
|
||||
"ASSISTANT": "OWNER"
|
||||
},
|
||||
"flags": {
|
||||
"hbm-rpg-v3-lore": {
|
||||
"category": "vault"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"packFolders": [
|
||||
{
|
||||
"name": "Lore — HbM RPG v3",
|
||||
"sorting": "a",
|
||||
"packs": [
|
||||
"core-rules-lore",
|
||||
"magic-book-lore",
|
||||
"bestiary-lore",
|
||||
"blood-magic-history",
|
||||
"crimson-cult",
|
||||
"abyss",
|
||||
"economy",
|
||||
"humanity-guide"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Świat — HbM RPG v3",
|
||||
"sorting": "a",
|
||||
"packs": [
|
||||
"disciplines-lore",
|
||||
"organizations",
|
||||
"races-lore",
|
||||
"classes",
|
||||
"concepts",
|
||||
"locations",
|
||||
"npcs",
|
||||
"conflicts",
|
||||
"player-characters",
|
||||
"adventures"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "hbm-rpg-v3-lore",
|
||||
"version": "1.3.4",
|
||||
"description": "Foundry VTT lore companion module for Homebrew Magic: RPG v3.",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:packs": "bun scripts/build-journal-packs.ts",
|
||||
"build:packs:strict": "bun scripts/build-journal-packs.ts --strict",
|
||||
"lint:uuid-refs": "bun scripts/lint-uuid-refs.ts",
|
||||
"package": "bun scripts/bump-version.ts && bun run build:packs && bun scripts/package.ts",
|
||||
"push": "bun scripts/push-package.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@foundryvtt/foundryvtt-cli": "^3.0.3",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"archiver": "^7.0.1",
|
||||
"typescript": "^5.5.0"
|
||||
},
|
||||
"license": "SEE LICENSE IN LICENSE.txt"
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Build LevelDB JournalEntry packs from book chapters.
|
||||
*
|
||||
* One pack per book / topic (mapped via PACK_CONFIGS + TOPICAL_CONFIGS).
|
||||
*
|
||||
* Split-book packs accept multiple source files: a list of `SourceFile`
|
||||
* entries each pointing at a `rules/` or `lore/` markdown file (instead of a
|
||||
* single monolithic `_books/` file). An optional `prefaceTitle` per source
|
||||
* captures the introductory content before the first chapter heading.
|
||||
*
|
||||
* Run: `bun scripts/build-journal-packs.ts` from `.src/foundry-lore/`.
|
||||
*/
|
||||
|
||||
import { mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, statSync, readFileSync } from 'node:fs';
|
||||
import { dirname, resolve, basename, extname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { compilePack } from '@foundryvtt/foundryvtt-cli';
|
||||
import { parseJournalBook, type JournalDoc, type JournalPage } from './parsers/journal-parser';
|
||||
import { parseTopicalFolder } from './parsers/topical-parser';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const moduleRoot = resolve(__dirname, '..');
|
||||
const repoRoot = resolve(moduleRoot, '..', '..');
|
||||
const booksDir = resolve(repoRoot, '_books');
|
||||
const packsSrcDir = resolve(moduleRoot, 'packs-src');
|
||||
const packsOutDir = resolve(moduleRoot, 'packs');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface SourceFile {
|
||||
/** Path relative to repoRoot. */
|
||||
path: string;
|
||||
/**
|
||||
* If set, lines before the first chapter heading are collected and emitted
|
||||
* as a synthetic journal entry with this title (e.g. "Wstęp").
|
||||
*/
|
||||
prefaceTitle?: string;
|
||||
}
|
||||
|
||||
interface PackConfig {
|
||||
pack: string;
|
||||
/** For provenance / flags. */
|
||||
sourceBook: string;
|
||||
/** One or more source files that together make up this pack. */
|
||||
sources: SourceFile[];
|
||||
/** Skip chapters whose title matches this regex. */
|
||||
skip?: RegExp;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pack configurations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const PACK_CONFIGS: PackConfig[] = [
|
||||
{
|
||||
pack: 'core-rules-lore',
|
||||
sourceBook: 'core-rules',
|
||||
sources: [{ path: 'ObsidianNotes/rules/00. Podręcznik Gry.md' }],
|
||||
skip: /^(Talenty|Zaklęcia|Atrybuty|Umiejętności|Wyposażenie)/i,
|
||||
},
|
||||
{
|
||||
pack: 'bestiary-lore',
|
||||
sourceBook: 'bestiary',
|
||||
sources: [{ path: 'ObsidianNotes/rules/06. Bestiariusz.md' }],
|
||||
skip: /^Talenty/i,
|
||||
},
|
||||
|
||||
// ── Split books: combine lore/ chapters + Wstęp intro from rules/ ──
|
||||
{
|
||||
pack: 'magic-book-lore',
|
||||
sourceBook: 'magic-book',
|
||||
sources: [
|
||||
// Lore intro lives inside the rules file (no dedicated lore/ file).
|
||||
{ path: 'ObsidianNotes/rules/01. Księga Magii.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^(Zaklęcia|Lista zaklęć|Rozdział V)/i,
|
||||
},
|
||||
{
|
||||
pack: 'abyss',
|
||||
sourceBook: 'abyss-curse',
|
||||
sources: [
|
||||
// Pure lore narrative.
|
||||
{ path: 'ObsidianNotes/lore/02. Otchłań i Magia.md' },
|
||||
// Rules intro (Wstęp only — mechanics chapters are skipped).
|
||||
{ path: 'ObsidianNotes/rules/02. Klątwa Otchłani.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^(Rozdział II - Talenty|Rozdział III - Dary|Rozdział IV - Choroby|Rozdział V - Zaklęcia|Rozdział X - Artefakty)/i,
|
||||
},
|
||||
{
|
||||
pack: 'blood-magic-history',
|
||||
sourceBook: 'arcanum-sanguinis',
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/rules/04. Arcanum Sanguinis.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^Rozdział IV - Talenty/i,
|
||||
},
|
||||
{
|
||||
pack: 'crimson-cult',
|
||||
sourceBook: 'crimson-cult',
|
||||
sources: [
|
||||
// Lore narrative (former Chwała Szkarłatnemu Kultowi lore section).
|
||||
{ path: 'ObsidianNotes/lore/05. Szkarłatny Kult.md' },
|
||||
// Rules intro only (spells/artifacts skipped).
|
||||
{ path: 'ObsidianNotes/rules/05. Vivat Patriarcha coccineus!.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^(Rozdział I - Zaklęcia|Rozdział II - Artefakty)/i,
|
||||
},
|
||||
{
|
||||
pack: 'economy',
|
||||
sourceBook: 'gold-steel-magic',
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/lore/03. Ekonomia Magicznego Świata.md' },
|
||||
{ path: 'ObsidianNotes/rules/03. Złoto, Stal i Magia.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^(Aneks A - Nowe Talenty|Aneks B - Przedmioty)/i,
|
||||
},
|
||||
|
||||
// ── New: Humanity Guide ──
|
||||
{
|
||||
pack: 'humanity-guide',
|
||||
sourceBook: 'humanity-guide',
|
||||
sources: [{ path: 'ObsidianNotes/lore/10. Przewodnik Ludzkości po Magicznym Świecie.md' }],
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Topical vault-folder configurations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface TopicalConfig {
|
||||
/** Folder path relative to repoRoot. */
|
||||
folder: string;
|
||||
/** Foundry pack id. */
|
||||
pack: string;
|
||||
}
|
||||
|
||||
const TOPICAL_CONFIGS: TopicalConfig[] = [
|
||||
{ folder: 'ObsidianNotes/disciplines', pack: 'disciplines-lore' },
|
||||
{ folder: 'ObsidianNotes/organizations', pack: 'organizations' },
|
||||
{ folder: 'ObsidianNotes/races', pack: 'races-lore' },
|
||||
{ folder: 'ObsidianNotes/classes', pack: 'classes' },
|
||||
{ folder: 'ObsidianNotes/concepts', pack: 'concepts' },
|
||||
{ folder: 'ObsidianNotes/locations', pack: 'locations' },
|
||||
{ folder: 'ObsidianNotes/npcs', pack: 'npcs' },
|
||||
{ folder: 'ObsidianNotes/conflicts', pack: 'conflicts' },
|
||||
{ folder: 'ObsidianNotes/player-characters', pack: 'player-characters' },
|
||||
{ folder: 'ObsidianNotes/adventures', pack: 'adventures' },
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
console.log(`[lore] repo root: ${repoRoot}`);
|
||||
|
||||
const allDocs: JournalDoc[] = [];
|
||||
|
||||
// ── Book packs ──
|
||||
for (const cfg of PACK_CONFIGS) {
|
||||
let packDocs: JournalDoc[] = [];
|
||||
for (const src of cfg.sources) {
|
||||
const bookPath = resolve(repoRoot, src.path);
|
||||
if (!existsSync(bookPath)) {
|
||||
console.warn(` · skip source ${src.path}: file not found`);
|
||||
continue;
|
||||
}
|
||||
const docs = parseJournalBook({
|
||||
bookPath,
|
||||
pack: cfg.pack,
|
||||
sourceBook: cfg.sourceBook,
|
||||
chapterSkip: cfg.skip,
|
||||
prefaceTitle: src.prefaceTitle,
|
||||
});
|
||||
|
||||
packDocs.push(...docs);
|
||||
}
|
||||
console.log(` · ${cfg.pack}: ${packDocs.length} entries`);
|
||||
allDocs.push(...packDocs);
|
||||
}
|
||||
|
||||
// ── Topical packs ──
|
||||
for (const top of TOPICAL_CONFIGS) {
|
||||
const folderPath = resolve(repoRoot, top.folder);
|
||||
if (!existsSync(folderPath)) {
|
||||
console.warn(` · skip topical ${top.pack}: folder not found (${top.folder})`);
|
||||
continue;
|
||||
}
|
||||
const docs = parseTopicalFolder({ folderPath, pack: top.pack });
|
||||
console.log(` · ${top.pack}: ${docs.length} entries`);
|
||||
allDocs.push(...docs);
|
||||
}
|
||||
|
||||
console.log(`[lore] parsed ${allDocs.length} journal entries total`);
|
||||
|
||||
if (existsSync(packsSrcDir)) rmSync(packsSrcDir, { recursive: true });
|
||||
const byPack = new Map<string, JournalDoc[]>();
|
||||
for (const doc of allDocs) {
|
||||
const list = byPack.get(doc.pack) ?? [];
|
||||
list.push(doc);
|
||||
byPack.set(doc.pack, list);
|
||||
}
|
||||
|
||||
for (const [pack, docs] of byPack) {
|
||||
const dir = resolve(packsSrcDir, pack);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
let sort = 0;
|
||||
for (const doc of docs) {
|
||||
sort += 100;
|
||||
const foundryDoc = toFoundryJournal(doc, sort);
|
||||
writeFileSync(resolve(dir, `${doc.id}.json`), `${JSON.stringify(foundryDoc, null, 2)}\n`, 'utf8');
|
||||
}
|
||||
console.log(` · wrote ${pack}: ${docs.length} entries`);
|
||||
}
|
||||
|
||||
if (existsSync(packsOutDir)) rmSync(packsOutDir, { recursive: true });
|
||||
for (const pack of byPack.keys()) {
|
||||
const src = resolve(packsSrcDir, pack);
|
||||
const dest = resolve(packsOutDir, pack);
|
||||
await compilePack(src, dest, {
|
||||
recursive: false,
|
||||
log: false,
|
||||
transformEntry: (doc: any, context: any) => {
|
||||
if (!doc._key) {
|
||||
console.error(`[build-journal-packs] ERROR: Document missing _key! Name: ${doc.name}, ID: ${doc._id}`);
|
||||
}
|
||||
if (doc.pages) {
|
||||
for (const page of doc.pages) {
|
||||
if (!page._key) {
|
||||
console.error(`[build-journal-packs] ERROR: Page missing _key! Journal: ${doc.name}, Page: ${page.name}, Page ID: ${page._id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
console.log(` ✓ compiled ${pack}`);
|
||||
}
|
||||
|
||||
console.log(`[lore] done — ${byPack.size} packs in packs/`);
|
||||
|
||||
function toFoundryJournal(doc: JournalDoc, sort: number): Record<string, unknown> {
|
||||
const fId = makeFoundryId(doc.id);
|
||||
return {
|
||||
_key: `!journal!${fId}`,
|
||||
_id: fId,
|
||||
name: doc.name,
|
||||
folder: null,
|
||||
sort,
|
||||
pages: doc.pages.map((page, i) => {
|
||||
const pageFId = makeFoundryId(`${doc.id}-page-${i}`);
|
||||
return {
|
||||
_key: `!journal.pages!${fId}.${pageFId}`,
|
||||
_id: pageFId,
|
||||
name: page.name,
|
||||
type: 'text',
|
||||
title: { show: true, level: 1 },
|
||||
text: { content: page.html, format: 1 /* HTML */ },
|
||||
sort: page.sort,
|
||||
};
|
||||
}),
|
||||
flags: {
|
||||
'hbm-rpg-v3-lore': {
|
||||
slug: doc.id,
|
||||
sourceBook: doc.source.book,
|
||||
sourceChapter: doc.source.chapter,
|
||||
sourceLine: doc.source.line,
|
||||
},
|
||||
},
|
||||
_stats: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a stable 12-character Foundry document ID from a slug.
|
||||
* Uses two independent FNV-1a 32-bit hashes to spread entropy across the
|
||||
* *entire* slug (not just the first 12 bytes), avoiding collisions between
|
||||
* slugs that share a long common prefix (e.g. page-0 vs page-1).
|
||||
*/
|
||||
function makeFoundryId(slug: string): string {
|
||||
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
// FNV-1a 32-bit, two independent passes with different offsets
|
||||
let h1 = 0x811c9dc5;
|
||||
let h2 = 0x4b9ace3f;
|
||||
for (let i = 0; i < slug.length; i++) {
|
||||
const c = slug.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0;
|
||||
h2 = Math.imul(h2 ^ (c + i + 1), 0x01000193) >>> 0;
|
||||
}
|
||||
// Encode 64 bits (two 32-bit hashes) into 12 base-62 chars
|
||||
let out = '';
|
||||
let lo = h1;
|
||||
let hi = h2;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
// Combine both halves cycling
|
||||
const combined = (i % 2 === 0 ? lo : hi) >>> 0;
|
||||
out += alphabet[combined % alphabet.length];
|
||||
lo = Math.imul(lo, 1664525) + 1013904223 >>> 0;
|
||||
hi = Math.imul(hi, 22695477) + 1013904223 >>> 0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Bumps the patch version in package.json and module.json in lock-step.
|
||||
* Run automatically as part of `bun run package`.
|
||||
*
|
||||
* Usage:
|
||||
* bun scripts/bump-version.ts # patch bump (0.1.0 → 0.1.1)
|
||||
* bun scripts/bump-version.ts minor # minor bump (0.1.0 → 0.2.0)
|
||||
* bun scripts/bump-version.ts major # major bump (0.1.0 → 1.0.0)
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, '..');
|
||||
|
||||
type BumpType = 'major' | 'minor' | 'patch';
|
||||
const bump = (process.argv[2] ?? 'patch') as BumpType;
|
||||
|
||||
function bumpVersion(version: string, type: BumpType): string {
|
||||
const [major, minor, patch] = version.split('.').map(Number);
|
||||
if (type === 'major') return `${major + 1}.0.0`;
|
||||
if (type === 'minor') return `${major}.${minor + 1}.0`;
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
// --- package.json ---
|
||||
const pkgPath = resolve(root, 'package.json');
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string };
|
||||
const oldVersion = pkg.version;
|
||||
const newVersion = bumpVersion(oldVersion, bump);
|
||||
pkg.version = newVersion;
|
||||
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
||||
|
||||
// --- module.json ---
|
||||
const modPath = resolve(root, 'module.json');
|
||||
const mod = JSON.parse(readFileSync(modPath, 'utf8')) as { version: string; download: string };
|
||||
mod.version = newVersion;
|
||||
// Update the versioned filename in the download URL if it follows the vX.Y.Z pattern
|
||||
mod.download = mod.download.replace(/-v[\d.]+\.zip$/, `-v${newVersion}.zip`);
|
||||
writeFileSync(modPath, JSON.stringify(mod, null, 2) + '\n');
|
||||
|
||||
console.log(`✓ Version bumped ${oldVersion} → ${newVersion}`);
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Lint @UUID[…] references across packs-src to catch broken cross-links
|
||||
* before packaging. Walks every JSON page in packs-src/, scans for
|
||||
* `@UUID[Compendium.<pkg>.<pack>...]` patterns, and reports any reference
|
||||
* to a system pack we know about but a slug that does not exist.
|
||||
*
|
||||
* Currently informational only — the journal parser does not yet emit
|
||||
* UUID cross-links automatically. This script provides the framework
|
||||
* for Phase 7.3/7.4 integration.
|
||||
*/
|
||||
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const moduleRoot = resolve(__dirname, '..');
|
||||
const packsSrcDir = resolve(moduleRoot, 'packs-src');
|
||||
|
||||
const UUID_RE = /@UUID\[Compendium\.([^.\s]+)\.([^.\s]+)\.([^.\s]+)\.([^\]\s]+)\]/g;
|
||||
|
||||
let scanned = 0;
|
||||
let total = 0;
|
||||
const refs: Array<{ file: string; pkg: string; pack: string; type: string; id: string }> = [];
|
||||
|
||||
function walk(dir: string) {
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const p = resolve(dir, entry);
|
||||
const s = statSync(p);
|
||||
if (s.isDirectory()) walk(p);
|
||||
else if (entry.endsWith('.json')) scanFile(p);
|
||||
}
|
||||
}
|
||||
|
||||
function scanFile(file: string) {
|
||||
scanned++;
|
||||
const text = readFileSync(file, 'utf8');
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = UUID_RE.exec(text)) !== null) {
|
||||
total++;
|
||||
refs.push({ file, pkg: m[1], pack: m[2], type: m[3], id: m[4] });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
walk(packsSrcDir);
|
||||
} catch {
|
||||
console.log('[lint-uuid] no packs-src/ — run build:packs first.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`[lint-uuid] scanned ${scanned} files; found ${total} @UUID refs.`);
|
||||
if (refs.length > 0) {
|
||||
for (const r of refs.slice(0, 25)) {
|
||||
console.log(` · ${r.pkg}.${r.pack}.${r.type}.${r.id}`);
|
||||
}
|
||||
if (refs.length > 25) console.log(` … (+${refs.length - 25} more)`);
|
||||
}
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Package the lore module into a versioned zip ready to drop into a
|
||||
* Foundry VTT instance's Data/modules/ directory.
|
||||
*/
|
||||
|
||||
import { createWriteStream, readFileSync } from 'node:fs';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import archiver from 'archiver';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, '..');
|
||||
const moduleJson = JSON.parse(readFileSync(resolve(root, 'module.json'), 'utf8')) as { id: string; version: string };
|
||||
const outFile = resolve(root, `${moduleJson.id}-v${moduleJson.version}.zip`);
|
||||
|
||||
await new Promise<void>((resolveAll, rejectAll) => {
|
||||
const output = createWriteStream(outFile);
|
||||
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||
|
||||
output.on('close', () => {
|
||||
console.log(`✓ Packaged ${outFile} (${archive.pointer()} bytes)`);
|
||||
resolveAll();
|
||||
});
|
||||
archive.on('warning', (err) => {
|
||||
if ((err as NodeJS.ErrnoException).code === 'ENOENT') console.warn(err);
|
||||
else rejectAll(err);
|
||||
});
|
||||
archive.on('error', rejectAll);
|
||||
|
||||
archive.pipe(output);
|
||||
// Foundry expects everything under a folder named after the module id.
|
||||
archive.file(resolve(root, 'module.json'), { name: `${moduleJson.id}/module.json` });
|
||||
archive.directory(resolve(root, 'lang'), `${moduleJson.id}/lang`);
|
||||
archive.directory(resolve(root, 'packs'), `${moduleJson.id}/packs`, (entry) => {
|
||||
if (entry.name.endsWith('LOCK')) return false;
|
||||
return entry;
|
||||
});
|
||||
archive.finalize();
|
||||
});
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Journal parser for HbM RPG v3 books.
|
||||
*
|
||||
* Strategy
|
||||
* ────────
|
||||
* Supports two file formats:
|
||||
*
|
||||
* A. Legacy Google-Docs export (`_books/`):
|
||||
* - No frontmatter.
|
||||
* - TOC is a plain-text block that reuses the same `Rozdział X — Title`
|
||||
* headings. The parser deduplicates: the SECOND occurrence of each
|
||||
* heading is the real body.
|
||||
*
|
||||
* B. Native Obsidian/repo files (`rules/`, `lore/`):
|
||||
* - Has YAML frontmatter (--- ... ---), stripped before parsing.
|
||||
* - TOC is Obsidian bullet-links (`- [[#Anchor|Label]]`), not bare
|
||||
* `Rozdział X` headings, so no duplicates exist and dedup is skipped.
|
||||
* - Chapters may be prefixed with markdown heading markers (`## `).
|
||||
* - An optional `prefaceTitle` captures content before the first chapter
|
||||
* as a synthetic "Wstęp" entry.
|
||||
*
|
||||
* Output: `JournalDoc[]` consumed by `build-journal-packs.ts`.
|
||||
*/
|
||||
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
export interface JournalDoc {
|
||||
/** Stable kebab-case slug. */
|
||||
id: string;
|
||||
/** Display title (Polish, as in book). */
|
||||
name: string;
|
||||
/** Pack id this entry belongs to (e.g. `abyss`). */
|
||||
pack: string;
|
||||
/** Source provenance for traceability. */
|
||||
source: { book: string; chapter: string; line: number };
|
||||
/** Pages — each becomes a `JournalEntryPage`. */
|
||||
pages: JournalPage[];
|
||||
}
|
||||
|
||||
export interface JournalPage {
|
||||
name: string;
|
||||
/** Content as HTML (paragraph-split markdown, basic conversion). */
|
||||
html: string;
|
||||
/** Hint for sort order. */
|
||||
sort: number;
|
||||
}
|
||||
|
||||
export interface JournalParseConfig {
|
||||
/** Absolute path to the markdown book. */
|
||||
bookPath: string;
|
||||
/** Pack id to assign (e.g. `abyss`). */
|
||||
pack: string;
|
||||
/** Source-book id for `source.book`. */
|
||||
sourceBook: string;
|
||||
/** Optional: only emit chapters whose title matches this regex. */
|
||||
chapterFilter?: RegExp;
|
||||
/** Optional: skip chapters whose title matches this regex (e.g. spell tables). */
|
||||
chapterSkip?: RegExp;
|
||||
/**
|
||||
* If set, content between the frontmatter/start and the first chapter is
|
||||
* collected and emitted as a synthetic entry with this title (e.g. "Wstęp").
|
||||
* Useful for the intro paragraphs of split `rules/` files.
|
||||
*/
|
||||
prefaceTitle?: string;
|
||||
}
|
||||
|
||||
// Matches both plain-text (`Rozdział IV - Title`) and heading-prefixed
|
||||
// (`## Rozdział IV - Title`) chapter lines. Optional trailing page number.
|
||||
const CHAPTER_RE = /^(?:#{1,6}\s+)?Rozdzia[łl]\s+([IVXLC]+)\s*[:.—\-–]\s*(.+?)(?:\s+\d+)?$/;
|
||||
const SEPARATOR = /^_{3,}$/;
|
||||
const FRONTMATTER_FENCE = /^---\s*$/;
|
||||
// Obsidian TOC bullets: `- [[#Anchor|Label]]` — skip in body content too
|
||||
const OBSIDIAN_TOC_BULLET = /^[-*]\s+\[\[#/;
|
||||
|
||||
/** Returns the first non-frontmatter line index (skips `--- ... ---` block). */
|
||||
function skipFrontmatter(lines: string[]): number {
|
||||
if (lines.length === 0 || !FRONTMATTER_FENCE.test(lines[0].trim())) return 0;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (FRONTMATTER_FENCE.test(lines[i].trim())) return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function parseJournalBook(cfg: JournalParseConfig): JournalDoc[] {
|
||||
const text = readFileSync(cfg.bookPath, 'utf8');
|
||||
const rawLines = text.split(/\r?\n/);
|
||||
const startIdx = skipFrontmatter(rawLines);
|
||||
const lines = rawLines.slice(startIdx);
|
||||
|
||||
// Pass 1: find every chapter occurrence.
|
||||
const occurrences: Array<{ idx: number; roman: string; title: string; raw: string }> = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const m = lines[i].trim().match(CHAPTER_RE);
|
||||
if (m) occurrences.push({ idx: i, roman: m[1], title: m[2].trim(), raw: lines[i].trim() });
|
||||
}
|
||||
if (occurrences.length === 0) {
|
||||
// No chapters found — if there's a prefaceTitle, still try to emit preface.
|
||||
if (cfg.prefaceTitle) {
|
||||
const pages = bodyToPages(lines);
|
||||
if (pages.length > 0) {
|
||||
const slug = slugify(cfg.prefaceTitle);
|
||||
return [{ id: `${cfg.pack}-${slug}`, name: cfg.prefaceTitle, pack: cfg.pack, source: { book: cfg.sourceBook, chapter: cfg.prefaceTitle, line: startIdx + 1 }, pages }];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Identify TOC entries: a chapter occurrence is part of the TOC if the
|
||||
// SAME (roman, title) pair occurs again later in the file.
|
||||
const seen = new Map<string, number>();
|
||||
const bodyChapters: typeof occurrences = [];
|
||||
for (const occ of occurrences) {
|
||||
const key = `${occ.roman}|${occ.title}`;
|
||||
if (!seen.has(key)) {
|
||||
seen.set(key, occ.idx);
|
||||
continue;
|
||||
}
|
||||
bodyChapters.push(occ);
|
||||
}
|
||||
// If no duplicates were found (no plain-text TOC — typical of native files),
|
||||
// treat the entire list as body chapters.
|
||||
const chapters = bodyChapters.length > 0 ? bodyChapters : occurrences;
|
||||
|
||||
const docs: JournalDoc[] = [];
|
||||
|
||||
// Emit preface entry (content before first chapter heading).
|
||||
if (cfg.prefaceTitle && chapters.length > 0) {
|
||||
const prefaceBody = lines.slice(0, chapters[0].idx);
|
||||
// Filter out Obsidian TOC bullets so they don't become page content.
|
||||
const prefaceClean = prefaceBody.filter((l) => !OBSIDIAN_TOC_BULLET.test(l.trim()));
|
||||
const pages = bodyToPages(prefaceClean);
|
||||
if (pages.length > 0) {
|
||||
const slug = slugify(cfg.prefaceTitle);
|
||||
docs.push({
|
||||
id: `${cfg.pack}-${slug}`,
|
||||
name: cfg.prefaceTitle,
|
||||
pack: cfg.pack,
|
||||
source: { book: cfg.sourceBook, chapter: cfg.prefaceTitle, line: startIdx + 1 },
|
||||
pages,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (let c = 0; c < chapters.length; c++) {
|
||||
const ch = chapters[c];
|
||||
if (cfg.chapterSkip && cfg.chapterSkip.test(ch.title)) continue;
|
||||
if (cfg.chapterFilter && !cfg.chapterFilter.test(ch.title)) continue;
|
||||
|
||||
const start = ch.idx + 1;
|
||||
const end = c + 1 < chapters.length ? chapters[c + 1].idx : lines.length;
|
||||
const bodyRaw = lines.slice(start, end);
|
||||
// Filter out Obsidian TOC bullets.
|
||||
const body = bodyRaw.filter((l) => !OBSIDIAN_TOC_BULLET.test(l.trim()));
|
||||
|
||||
const pages = bodyToPages(body);
|
||||
if (pages.length === 0) continue;
|
||||
|
||||
const slug = slugify(`${ch.roman}-${ch.title}`);
|
||||
docs.push({
|
||||
id: `${cfg.pack}-${slug}`,
|
||||
name: `Rozdział ${ch.roman} — ${ch.title}`,
|
||||
pack: cfg.pack,
|
||||
source: { book: cfg.sourceBook, chapter: ch.title, line: startIdx + ch.idx + 1 },
|
||||
pages,
|
||||
});
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
/** Split a chapter body by `____` separators into pages, converting each to HTML. */
|
||||
function bodyToPages(body: string[]): JournalPage[] {
|
||||
const segments: string[][] = [[]];
|
||||
for (const line of body) {
|
||||
if (SEPARATOR.test(line.trim())) {
|
||||
if (segments[segments.length - 1].length > 0) segments.push([]);
|
||||
} else {
|
||||
segments[segments.length - 1].push(line);
|
||||
}
|
||||
}
|
||||
const pages: JournalPage[] = [];
|
||||
let pageNo = 1;
|
||||
for (const seg of segments) {
|
||||
const cleaned = trimEdges(seg);
|
||||
if (cleaned.length === 0) continue;
|
||||
const name = derivePageTitle(cleaned, pageNo);
|
||||
pages.push({ name, html: linesToHtml(cleaned), sort: pageNo * 100 });
|
||||
pageNo++;
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
function trimEdges(arr: string[]): string[] {
|
||||
let lo = 0;
|
||||
let hi = arr.length;
|
||||
while (lo < hi && arr[lo].trim() === '') lo++;
|
||||
while (hi > lo && arr[hi - 1].trim() === '') hi--;
|
||||
return arr.slice(lo, hi);
|
||||
}
|
||||
|
||||
/** First non-empty line if it looks short and title-like, else "Część N". */
|
||||
function derivePageTitle(lines: string[], pageNo: number): string {
|
||||
const first = lines.find((l) => l.trim().length > 0)?.trim() ?? '';
|
||||
if (first.length > 0 && first.length <= 80 && !/[.!?]$/.test(first)) {
|
||||
return first;
|
||||
}
|
||||
return `Część ${pageNo}`;
|
||||
}
|
||||
|
||||
/** Conservative markdown → HTML conversion. Paragraphs, bold, italic, lists. */
|
||||
function linesToHtml(lines: string[]): string {
|
||||
const out: string[] = [];
|
||||
let para: string[] = [];
|
||||
let listType: 'ul' | 'ol' | null = null;
|
||||
|
||||
const flushPara = () => {
|
||||
if (para.length === 0) return;
|
||||
out.push(`<p>${inline(para.join(' ').trim())}</p>`);
|
||||
para = [];
|
||||
};
|
||||
const flushList = () => {
|
||||
if (listType) {
|
||||
out.push(`</${listType}>`);
|
||||
listType = null;
|
||||
}
|
||||
};
|
||||
|
||||
for (const raw of lines) {
|
||||
const line = raw.trimEnd();
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (trimmed === '') {
|
||||
flushPara();
|
||||
flushList();
|
||||
continue;
|
||||
}
|
||||
|
||||
const ulMatch = trimmed.match(/^[*\-]\s+(.+)$/);
|
||||
const olMatch = trimmed.match(/^\d+\.\s+(.+)$/);
|
||||
if (ulMatch) {
|
||||
flushPara();
|
||||
if (listType !== 'ul') {
|
||||
flushList();
|
||||
listType = 'ul';
|
||||
out.push('<ul>');
|
||||
}
|
||||
out.push(`<li>${inline(ulMatch[1])}</li>`);
|
||||
continue;
|
||||
}
|
||||
if (olMatch) {
|
||||
flushPara();
|
||||
if (listType !== 'ol') {
|
||||
flushList();
|
||||
listType = 'ol';
|
||||
out.push('<ol>');
|
||||
}
|
||||
out.push(`<li>${inline(olMatch[1])}</li>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sub-heading: stand-alone short bold-ish line
|
||||
if (/^#{1,6}\s+/.test(trimmed)) {
|
||||
flushPara();
|
||||
flushList();
|
||||
const lvl = Math.min(6, (trimmed.match(/^#+/)?.[0].length ?? 2) + 1);
|
||||
out.push(`<h${lvl}>${inline(trimmed.replace(/^#+\s+/, ''))}</h${lvl}>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
flushList();
|
||||
para.push(line);
|
||||
}
|
||||
flushPara();
|
||||
flushList();
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
function inline(s: string): string {
|
||||
// Order matters: escape first, then markup.
|
||||
let r = s
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
r = r.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
r = r.replace(/(^|[\s(])\*([^*\n]+)\*/g, '$1<em>$2</em>');
|
||||
r = r.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, (_m, target, label) => {
|
||||
return `<a class="lore-link" data-target="${target.trim()}">${(label ?? target).trim()}</a>`;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
function slugify(s: string): string {
|
||||
return s
|
||||
.toLowerCase()
|
||||
.normalize('NFKD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/ł/g, 'l')
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
.slice(0, 60);
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* Topical vault-folder parser.
|
||||
*
|
||||
* Recursively walks a folder and converts every `.md` file into a
|
||||
* `JournalDoc`. Strips YAML frontmatter and Obsidian tag lines; converts the
|
||||
* remaining body to HTML pages (one per `____` separator, or a single page
|
||||
* if no separators). Empty/stub files still produce a JournalEntry with one
|
||||
* empty page so the folder layout mirrors the Obsidian vault in Foundry.
|
||||
*
|
||||
* Stable `_id` derivation: `slugify(packId + '/' + relativePathWithoutExt)`.
|
||||
*/
|
||||
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
||||
import { resolve, relative, basename, extname, dirname } from 'node:path';
|
||||
import type { JournalDoc, JournalPage } from './journal-parser';
|
||||
|
||||
export interface TopicalParseConfig {
|
||||
/** Absolute path to the folder to walk. */
|
||||
folderPath: string;
|
||||
/** Foundry pack id (e.g. `organizations`). */
|
||||
pack: string;
|
||||
}
|
||||
|
||||
const SEPARATOR = /^_{3,}$/;
|
||||
const FRONTMATTER_FENCE = /^---\s*$/;
|
||||
const OBSIDIAN_TOC_BULLET = /^[-*]\s+\[\[#/;
|
||||
// Obsidian tag lines: lines that start with `#tag` or are `#tag #tag2 …`
|
||||
const TAG_LINE = /^(#[a-zA-ZżźćąśęłóńŻŹĆĄŚĘŁÓŃ_-]+\s*)+$/;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function parseTopicalFolder(cfg: TopicalParseConfig): JournalDoc[] {
|
||||
const docs: JournalDoc[] = [];
|
||||
walkDir(cfg.folderPath, cfg.folderPath, cfg.pack, docs);
|
||||
return docs;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function walkDir(
|
||||
rootPath: string,
|
||||
currentPath: string,
|
||||
pack: string,
|
||||
out: JournalDoc[],
|
||||
): void {
|
||||
let entries: string[];
|
||||
try {
|
||||
entries = readdirSync(currentPath);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
// Skip hidden files / system files.
|
||||
if (entry.startsWith('.')) continue;
|
||||
|
||||
const fullPath = resolve(currentPath, entry);
|
||||
let stat;
|
||||
try {
|
||||
stat = statSync(fullPath);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
walkDir(rootPath, fullPath, pack, out);
|
||||
} else if (stat.isFile() && extname(entry).toLowerCase() === '.md') {
|
||||
const doc = parseTopicalFile(rootPath, fullPath, pack);
|
||||
if (doc) out.push(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseTopicalFile(
|
||||
rootPath: string,
|
||||
filePath: string,
|
||||
pack: string,
|
||||
): JournalDoc | null {
|
||||
let rawText = '';
|
||||
try {
|
||||
rawText = readFileSync(filePath, 'utf8');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rawLines = rawText.split(/\r?\n/);
|
||||
|
||||
// Strip YAML frontmatter.
|
||||
const bodyStart = skipFrontmatter(rawLines);
|
||||
const bodyLines = rawLines.slice(bodyStart);
|
||||
|
||||
// Derive stable id from relative path.
|
||||
const relPath = relative(rootPath, filePath);
|
||||
// e.g. "Federacja Sol-3/Diana.md" → "federacja-sol-3-diana"
|
||||
const slug = slugifyPath(relPath);
|
||||
const id = `${pack}-${slug}`;
|
||||
|
||||
// Display name = filename without extension.
|
||||
const name = basename(filePath, extname(filePath));
|
||||
|
||||
// Build pages from body (split on ___ separators); even stubs get one page.
|
||||
const pages = bodyToPages(bodyLines, name);
|
||||
|
||||
// Source: relative path inside the repo.
|
||||
const source = {
|
||||
book: pack,
|
||||
chapter: name,
|
||||
line: bodyStart + 1,
|
||||
};
|
||||
|
||||
return { id, name, pack, source, pages };
|
||||
}
|
||||
|
||||
/** Split body into pages separated by `____`. */
|
||||
function bodyToPages(lines: string[], defaultName: string): JournalPage[] {
|
||||
const segments: string[][] = [[]];
|
||||
for (const line of lines) {
|
||||
if (SEPARATOR.test(line.trim())) {
|
||||
segments.push([]);
|
||||
} else if (!OBSIDIAN_TOC_BULLET.test(line.trim())) {
|
||||
// Skip Obsidian TOC bullets from pages too.
|
||||
segments[segments.length - 1].push(line);
|
||||
}
|
||||
}
|
||||
|
||||
const pages: JournalPage[] = [];
|
||||
let pageNo = 1;
|
||||
for (const seg of segments) {
|
||||
const cleaned = trimEdges(seg.filter((l) => !TAG_LINE.test(l.trim())));
|
||||
// Always emit at least one page, even if empty (placeholders for stubs).
|
||||
const html = cleaned.length > 0 ? linesToHtml(cleaned) : '';
|
||||
const name = pages.length === 0 ? (derivePageTitle(cleaned) ?? defaultName) : `Część ${pageNo}`;
|
||||
pages.push({ name, html, sort: pageNo * 100 });
|
||||
pageNo++;
|
||||
// Only the first page can be empty (stub placeholder). Subsequent empty
|
||||
// segments (trailing separators) are skipped.
|
||||
if (cleaned.length === 0 && pageNo > 2) continue;
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
function trimEdges(arr: string[]): string[] {
|
||||
let lo = 0;
|
||||
let hi = arr.length;
|
||||
while (lo < hi && arr[lo].trim() === '') lo++;
|
||||
while (hi > lo && arr[hi - 1].trim() === '') hi--;
|
||||
return arr.slice(lo, hi);
|
||||
}
|
||||
|
||||
function derivePageTitle(lines: string[]): string | null {
|
||||
const first = lines.find((l) => l.trim().length > 0)?.trim() ?? '';
|
||||
if (!first) return null;
|
||||
const bare = first.replace(/^#{1,6}\s+/, '');
|
||||
if (bare.length > 0 && bare.length <= 80 && !/[.!?]$/.test(bare)) return bare;
|
||||
return null;
|
||||
}
|
||||
|
||||
function skipFrontmatter(lines: string[]): number {
|
||||
if (lines.length === 0 || !FRONTMATTER_FENCE.test(lines[0].trim())) return 0;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (FRONTMATTER_FENCE.test(lines[i].trim())) return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function slugifyPath(relPath: string): string {
|
||||
// Remove .md extension, convert path separators to hyphens, slugify.
|
||||
const withoutExt = relPath.replace(/\.md$/i, '');
|
||||
return withoutExt
|
||||
.toLowerCase()
|
||||
.normalize('NFKD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/ł/g, 'l')
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
.slice(0, 80);
|
||||
}
|
||||
|
||||
/** Conservative markdown → HTML. Reuses same logic as journal-parser. */
|
||||
function linesToHtml(lines: string[]): string {
|
||||
const out: string[] = [];
|
||||
let para: string[] = [];
|
||||
let listType: 'ul' | 'ol' | null = null;
|
||||
|
||||
const flushPara = () => {
|
||||
if (para.length === 0) return;
|
||||
out.push(`<p>${inline(para.join(' ').trim())}</p>`);
|
||||
para = [];
|
||||
};
|
||||
const flushList = () => {
|
||||
if (listType) {
|
||||
out.push(`</${listType}>`);
|
||||
listType = null;
|
||||
}
|
||||
};
|
||||
|
||||
for (const raw of lines) {
|
||||
const line = raw.trimEnd();
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (trimmed === '' || TAG_LINE.test(trimmed)) {
|
||||
flushPara();
|
||||
flushList();
|
||||
continue;
|
||||
}
|
||||
|
||||
const ulMatch = trimmed.match(/^[*\-]\s+(.+)$/);
|
||||
const olMatch = trimmed.match(/^\d+\.\s+(.+)$/);
|
||||
if (ulMatch) {
|
||||
flushPara();
|
||||
if (listType !== 'ul') { flushList(); listType = 'ul'; out.push('<ul>'); }
|
||||
out.push(`<li>${inline(ulMatch[1])}</li>`);
|
||||
continue;
|
||||
}
|
||||
if (olMatch) {
|
||||
flushPara();
|
||||
if (listType !== 'ol') { flushList(); listType = 'ol'; out.push('<ol>'); }
|
||||
out.push(`<li>${inline(olMatch[1])}</li>`);
|
||||
continue;
|
||||
}
|
||||
if (/^#{1,6}\s+/.test(trimmed)) {
|
||||
flushPara();
|
||||
flushList();
|
||||
const lvl = Math.min(6, (trimmed.match(/^#+/)?.[0].length ?? 2) + 1);
|
||||
out.push(`<h${lvl}>${inline(trimmed.replace(/^#+\s+/, ''))}</h${lvl}>`);
|
||||
continue;
|
||||
}
|
||||
flushList();
|
||||
para.push(line);
|
||||
}
|
||||
flushPara();
|
||||
flushList();
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
function inline(s: string): string {
|
||||
let r = s
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
r = r.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
r = r.replace(/(^|[\s(])\*([^*\n]+)\*/g, '$1<em>$2</em>');
|
||||
r = r.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, (_m, target, label) =>
|
||||
`<a class="lore-link" data-target="${target.trim()}">${(label ?? target).trim()}</a>`,
|
||||
);
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// 1. Resolve paths and load package manifest
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, '..');
|
||||
|
||||
let id = '';
|
||||
let version = '';
|
||||
|
||||
if (existsSync(resolve(root, 'module.json'))) {
|
||||
const moduleJson = JSON.parse(readFileSync(resolve(root, 'module.json'), 'utf8'));
|
||||
id = moduleJson.id;
|
||||
version = moduleJson.version;
|
||||
} else if (existsSync(resolve(root, 'system.json'))) {
|
||||
const systemJson = JSON.parse(readFileSync(resolve(root, 'system.json'), 'utf8'));
|
||||
id = systemJson.id;
|
||||
version = systemJson.version;
|
||||
} else if (existsSync(resolve(root, 'dist', 'system.json'))) {
|
||||
const systemJson = JSON.parse(readFileSync(resolve(root, 'dist', 'system.json'), 'utf8'));
|
||||
id = systemJson.id;
|
||||
version = systemJson.version;
|
||||
} else {
|
||||
console.error("Error: Could not find system.json or module.json manifest!");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const zipPath = resolve(root, `${id}-v${version}.zip`);
|
||||
|
||||
if (!existsSync(zipPath)) {
|
||||
console.error(`Error: Packaged zip file not found at ${zipPath}`);
|
||||
console.error("Please run 'bun run package' first to generate the zip package.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 2. Determine ingest URL and API token from environment variables
|
||||
const ingestUrl = process.env.FOUNDRY_INGEST_URL;
|
||||
const apiToken = process.env.FOUNDRY_INGEST_TOKEN;
|
||||
|
||||
if (!ingestUrl) {
|
||||
console.error("Error: FOUNDRY_INGEST_URL environment variable is not defined!");
|
||||
console.error("Please define FOUNDRY_INGEST_URL in your .env file.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!apiToken) {
|
||||
console.error("Error: FOUNDRY_INGEST_TOKEN environment variable is not defined!");
|
||||
console.error("Please define FOUNDRY_INGEST_TOKEN in your .env file.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Pushing package "${id}" version ${version}...`);
|
||||
console.log(`Target URL: ${ingestUrl}`);
|
||||
|
||||
// 3. Build multipart form data and perform upload
|
||||
const formData = new FormData();
|
||||
const fileBlob = Bun.file(zipPath);
|
||||
formData.append('file', fileBlob, `${id}-v${version}.zip`);
|
||||
|
||||
try {
|
||||
const response = await fetch(ingestUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-API-TOKEN': apiToken
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log("✓ Package successfully pushed and ingested!");
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
} else {
|
||||
console.error(`Error: Ingest server returned status ${response.status} ${response.statusText}`);
|
||||
const text = await response.text();
|
||||
console.error(`Response details: ${text}`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error connecting to ingest server:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ES2022"],
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["scripts/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user