@hexah/skin-sdk 0.2.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/LICENSE +21 -0
- package/README.md +47 -0
- package/index.js +117 -0
- package/package.json +30 -0
- package/runtime-stubs.js +142 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hexah Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @hexah/skin-sdk
|
|
2
|
+
|
|
3
|
+
Publiczny, wersjonowany **kontrakt SDK skórek Hexah** — stałe i typy, na których
|
|
4
|
+
deweloperzy budują własne podstrony (ekrany szablonu) i motywy, **bez dostępu do kodu gry**.
|
|
5
|
+
|
|
6
|
+
Pakiet jest celowo „chudy": zawiera wyłącznie kontrakt (zero implementacji, zero importów
|
|
7
|
+
z aplikacji, brak Reacta i kroku budowania). Implementacje read-modeli, usług platformy i
|
|
8
|
+
prymitywów UI dostarcza **host** w runtime (docelowo przez Module Federation; lokalnie w
|
|
9
|
+
repo: `frontend/common/skin-sdk/{runtime,ui,slots}.js`).
|
|
10
|
+
|
|
11
|
+
## Instalacja
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install --save-dev @hexah/skin-sdk
|
|
15
|
+
# peer-y dostarcza host w runtime; instalujesz je u siebie do builda/typowania:
|
|
16
|
+
npm install react react-dom jotai next @mui/material @mui/icons-material
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Co dostajesz
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { SCREEN_KEYS, SLOT_KEYS, DEFAULT_TEMPLATE, SKIN_API_VERSION } from '@hexah/skin-sdk'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- `SCREEN_KEYS` — klucze ekranów, pod które rejestrujesz komponenty skórki.
|
|
26
|
+
- `SLOT_KEYS` — klucze slotów (ciężkie fragmenty renderowane przez hosta; rozmieszczasz je).
|
|
27
|
+
- `DEFAULT_TEMPLATE` — szablon bazowy (fallback).
|
|
28
|
+
- `SKIN_API_VERSION` — wersja kontraktu, którą deklaruje skórka (host odrzuca niezgodny major).
|
|
29
|
+
- Typy JSDoc: `ReportListItem`, `ReportListView`, `ReportListResult`, `Pagination`, `SkinScreenProps`.
|
|
30
|
+
|
|
31
|
+
Read-modele/usługi (`useReports`, `useSnackbar`, `useDialog`, `useShard`…), prymitywy UI
|
|
32
|
+
(`PageBox`, `HexahChip`, `DataList`…) i `useSlots()` paczka udostępnia jako **typowane atrapy**
|
|
33
|
+
(`runtime-stubs.js`) — dają podpowiedzi w edytorze, a realne implementacje dostarcza host w
|
|
34
|
+
runtime (Module Federation, shareScope). Atrapa wywołana poza hostem rzuca jasny błąd: skórka
|
|
35
|
+
działa wyłącznie wewnątrz aplikacji Hexah. Bundler skórki dzieli te paczki z `import: false`,
|
|
36
|
+
więc atrapy nigdy nie trafiają do bundla.
|
|
37
|
+
|
|
38
|
+
## Wersjonowanie
|
|
39
|
+
|
|
40
|
+
`SKIN_API_VERSION` (semver): **major** = zmiana łamiąca powierzchnię, **minor** = dodanie
|
|
41
|
+
zgodne wstecz, **patch** = bez zmiany powierzchni.
|
|
42
|
+
|
|
43
|
+
## Dokumentacja
|
|
44
|
+
|
|
45
|
+
- Przewodnik dewelopera skórek: `docs/skin-sdk/README.md`
|
|
46
|
+
- Reguły: `docs/skin-sdk/SKIN_SDK_AI_RULES.md`
|
|
47
|
+
- Granica publiczne/prywatne: `docs/skin-sdk/inventory-publicznego-api.md`
|
package/index.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @hexah/skin-sdk — KONTRAKT (publiczne, wersjonowane API skórek)
|
|
5
|
+
*
|
|
6
|
+
* Jedyne źródło prawdy powierzchni, na której deweloperzy skórek budują własne podstrony,
|
|
7
|
+
* BEZ dostępu do kodu gry. Tu żyją wyłącznie stałe i typy (JSDoc) — zero implementacji,
|
|
8
|
+
* zero importów z aplikacji. Implementacje read-modeli/usług/prymitywów dostarcza host
|
|
9
|
+
* w runtime (in-repo: `frontend/common/skin-sdk/{runtime,ui,slots}.js`; docelowo przez
|
|
10
|
+
* Module Federation). Pakiet jest czystym CJS (interop z ESM frontu i `require` serwera),
|
|
11
|
+
* bez Reacta i bez kroku budowania.
|
|
12
|
+
*
|
|
13
|
+
* Granica publiczne/prywatne: patrz docs/skin-sdk/inventory-publicznego-api.md (sekcja F)
|
|
14
|
+
* oraz docs/skin-sdk/SKIN_SDK_AI_RULES.md.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Wersja kontraktu SDK. Skórka deklaruje, na jakiej wersji działa; host odrzuca niezgodny
|
|
19
|
+
* major. Bump: major = zmiana łamiąca powierzchnię, minor = dodanie zgodne wstecz,
|
|
20
|
+
* patch = bez zmiany powierzchni.
|
|
21
|
+
*/
|
|
22
|
+
const SKIN_API_VERSION = "0.2.0";
|
|
23
|
+
|
|
24
|
+
/** Domyślny szablon, gdy motyw nie ma własnego wariantu wizualnego. */
|
|
25
|
+
const DEFAULT_TEMPLATE = "medieval";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Klucze ekranów, pod które szablon (skórka) dostarcza komponenty prezentacyjne.
|
|
29
|
+
* Musi pokrywać się z `frontend/common/templates/templateResolution.js` — pilnuje tego
|
|
30
|
+
* test dryfu (`frontend/tests/unit/skinSdkPackageDrift.test.js`).
|
|
31
|
+
*/
|
|
32
|
+
const SCREEN_KEYS = Object.freeze({
|
|
33
|
+
ARTICLE: "article",
|
|
34
|
+
KURIER: "kurier",
|
|
35
|
+
KURIER_REDAKCJA: "kurierRedakcja",
|
|
36
|
+
KURIER_SKRZYNKA: "kurierSkrzynka",
|
|
37
|
+
GPT_LIST: "gptList",
|
|
38
|
+
GPT_THREAD: "gptThread",
|
|
39
|
+
REPORT: "report",
|
|
40
|
+
REPORT_DETAIL: "reportDetail",
|
|
41
|
+
ARTICLE_DETAIL: "articleDetail",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Klucze slotów — fragmenty z logiką/danymi renderowane przez HOST. Skórka dostaje je
|
|
46
|
+
* jako gotowe komponenty (przez `useSlots()`) i tylko rozmieszcza; nie ma dostępu do
|
|
47
|
+
* ich wnętrza ani do kodu gry.
|
|
48
|
+
* @see docs/skin-sdk/inventory-publicznego-api.md sekcja F (b)
|
|
49
|
+
*/
|
|
50
|
+
const SLOT_KEYS = Object.freeze({
|
|
51
|
+
GAME_PAGE: "gamePage",
|
|
52
|
+
ISSUE_RICH_CONTENT: "issueRichContent",
|
|
53
|
+
ISSUE_STATS: "issueStats",
|
|
54
|
+
ARTICLE_COMMENTS: "articleComments",
|
|
55
|
+
WORLD_MAP: "worldMap",
|
|
56
|
+
ARTICLE_ISSUE_GRID: "articleIssueGrid",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @typedef {object} ReportListItem — wiersz listy zgłoszeń (read-model, bez wewnętrznych pól).
|
|
61
|
+
* @property {number} id
|
|
62
|
+
* @property {number} issueNumber
|
|
63
|
+
* @property {string} title
|
|
64
|
+
* @property {'open'|'closed'} state
|
|
65
|
+
* @property {string|null} issueType
|
|
66
|
+
* @property {boolean} isNew
|
|
67
|
+
* @property {boolean} hasNewComments
|
|
68
|
+
* @property {boolean} plannedForImplementation
|
|
69
|
+
* @property {number} voteScore
|
|
70
|
+
* @property {number} commentsCount
|
|
71
|
+
* @property {number} subscribersCount
|
|
72
|
+
* @property {string} githubCreatedAt
|
|
73
|
+
* @property {{ user?: { username?: string, documentId?: string } }} [shardUser]
|
|
74
|
+
* @property {string} [githubAuthor]
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @typedef {object} ReportListView — filtry/widok listy zgłoszeń.
|
|
79
|
+
* @property {number} [page]
|
|
80
|
+
* @property {string} [search]
|
|
81
|
+
* @property {'all'|'open'|'closed'} [state]
|
|
82
|
+
* @property {'all'|'Bug'|'Feature'|'Pytanie'} [type]
|
|
83
|
+
* @property {boolean} [mine]
|
|
84
|
+
* @property {boolean} [planned]
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @typedef {object} Pagination
|
|
89
|
+
* @property {number} page
|
|
90
|
+
* @property {number} pageSize
|
|
91
|
+
* @property {number} total
|
|
92
|
+
* @property {number} pageCount
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {object} ReportListResult
|
|
97
|
+
* @property {ReportListItem[]} issues
|
|
98
|
+
* @property {Pagination} pagination
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Kontrakt propsów ekranu skórki (komponent zarejestrowany pod `SCREEN_KEYS`).
|
|
103
|
+
* Sloty (gotowe komponenty hosta) przychodzą w `slots`.
|
|
104
|
+
* @typedef {object} SkinScreenProps
|
|
105
|
+
* @property {Record<string, import('react').ComponentType<object>>} [slots]
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
// Typowane atrapy runtime (hooki/UI/sloty/utile) — implementacje dostarcza host w runtime.
|
|
109
|
+
const runtimeStubs = require("./runtime-stubs");
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
SKIN_API_VERSION,
|
|
113
|
+
DEFAULT_TEMPLATE,
|
|
114
|
+
SCREEN_KEYS,
|
|
115
|
+
SLOT_KEYS,
|
|
116
|
+
...runtimeStubs,
|
|
117
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hexah/skin-sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Publiczny, wersjonowany kontrakt SDK skórek Hexah (stałe + typy). Implementacje dostarcza host w runtime.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.js",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Hexah Team <kontakt@hexah.pl>",
|
|
9
|
+
"homepage": "https://github.com/Kroniki-Fallathanu/hexah/tree/develop/packages/skin-sdk#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Kroniki-Fallathanu/hexah.git",
|
|
13
|
+
"directory": "packages/skin-sdk"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"hexah",
|
|
17
|
+
"skin",
|
|
18
|
+
"sdk",
|
|
19
|
+
"module-federation"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
"index.js",
|
|
23
|
+
"runtime-stubs.js",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/runtime-stubs.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @hexah/skin-sdk — STUBY RUNTIME (typy dla deweloperów skórek)
|
|
5
|
+
*
|
|
6
|
+
* Hooki, prymitywy UI i sloty NIE są implementowane w paczce — dostarcza je HOST w runtime
|
|
7
|
+
* przez Module Federation (shareScope, klucz `@hexah/skin-sdk` → barrel `@/common/skin-sdk`).
|
|
8
|
+
* Tu żyją wyłącznie **typowane atrapy**: dają deweloperowi skórki podpowiedzi w edytorze i
|
|
9
|
+
* pozwalają zbudować skórkę względem stabilnej powierzchni. Atrapa wywołana POZA hostem rzuca
|
|
10
|
+
* jasny błąd (skórka działa tylko wewnątrz hosta). Bundler skórki dzieli te paczki z
|
|
11
|
+
* `import: false`, więc atrapy nigdy nie trafiają do bundla — w runtime używana jest
|
|
12
|
+
* implementacja hosta.
|
|
13
|
+
*
|
|
14
|
+
* Zgodność tej powierzchni z barrelem hosta pilnuje test dryfu
|
|
15
|
+
* (`frontend/tests/unit/skinSdkPackageDrift.test.js`).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Tworzy typowaną atrapę „dostarczane przez host". Wywołanie poza hostem rzuca.
|
|
20
|
+
* @param {string} name
|
|
21
|
+
* @returns {(...args: unknown[]) => never}
|
|
22
|
+
*/
|
|
23
|
+
function hostProvided(name) {
|
|
24
|
+
return function hostProvidedStub() {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`@hexah/skin-sdk: „${name}" dostarcza host w runtime (Module Federation). ` +
|
|
27
|
+
`Nie wywołuj/nie renderuj go poza hostem — skórka działa wyłącznie wewnątrz aplikacji Hexah.`,
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ---------- Read-modele (host) ---------- */
|
|
33
|
+
/** @returns {object|null} Aktywny shard (świat). */
|
|
34
|
+
const useShard = hostProvided("useShard");
|
|
35
|
+
/** @returns {object|null} Shard-user (konto na shardzie). */
|
|
36
|
+
const useShardUser = hostProvided("useShardUser");
|
|
37
|
+
/** @returns {object|null} Aktywna postać. */
|
|
38
|
+
const useCharacter = hostProvided("useCharacter");
|
|
39
|
+
/** @returns {object|null} Konto gracza. */
|
|
40
|
+
const useAccount = hostProvided("useAccount");
|
|
41
|
+
/** @returns {object|null} Dane strony (powłoka). */
|
|
42
|
+
const usePageData = hostProvided("usePageData");
|
|
43
|
+
/** @returns {Array} Postacie online na shardzie. */
|
|
44
|
+
const useOnlineCharacters = hostProvided("useOnlineCharacters");
|
|
45
|
+
|
|
46
|
+
/* ---------- Usługi platformy (host) ---------- */
|
|
47
|
+
/** @returns {{ notify: (messages: Array<{severity:string,message:string}>) => void }} */
|
|
48
|
+
const useSnackbar = hostProvided("useSnackbar");
|
|
49
|
+
/** @returns {{ open: (config: object) => void, close: () => void }} */
|
|
50
|
+
const useDialog = hostProvided("useDialog");
|
|
51
|
+
/**
|
|
52
|
+
* @returns {{
|
|
53
|
+
* list: (data: import('./index').ReportListView & { pageSize?: number, callback: (r:any)=>void }) => void,
|
|
54
|
+
* markAllRead: (callback: (r:any)=>void) => void,
|
|
55
|
+
* setIssueNotification: (next: boolean, callback: (r:any)=>void) => void,
|
|
56
|
+
* }}
|
|
57
|
+
*/
|
|
58
|
+
const useReports = hostProvided("useReports");
|
|
59
|
+
/** @param {{ title?: string }} shell Ustawia tytuł/powłokę strony. @returns {void} */
|
|
60
|
+
const useGamePageShell = hostProvided("useGamePageShell");
|
|
61
|
+
|
|
62
|
+
/* ---------- Sloty (host) ---------- */
|
|
63
|
+
/** Provider slotów hosta (host montuje go sam; skórka go nie używa). */
|
|
64
|
+
const SlotsProvider = hostProvided("SlotsProvider");
|
|
65
|
+
/** @returns {Record<string, import('react').ComponentType<object>>} Mapa slotów hosta. */
|
|
66
|
+
const useSlots = hostProvided("useSlots");
|
|
67
|
+
|
|
68
|
+
/* ---------- Prymitywy UI (host) ---------- */
|
|
69
|
+
/** @type {import('react').ComponentType<object>} */
|
|
70
|
+
const PageBox = hostProvided("PageBox");
|
|
71
|
+
/** @type {import('react').ComponentType<object>} */
|
|
72
|
+
const HexahCharacterSection = hostProvided("HexahCharacterSection");
|
|
73
|
+
/** @type {import('react').ComponentType<object>} */
|
|
74
|
+
const AngularPanel = hostProvided("AngularPanel");
|
|
75
|
+
/** @type {import('react').ComponentType<object>} */
|
|
76
|
+
const StandardButton = hostProvided("StandardButton");
|
|
77
|
+
/** @type {import('react').ComponentType<object>} */
|
|
78
|
+
const RoundAvatar = hostProvided("RoundAvatar");
|
|
79
|
+
/** @type {import('react').ComponentType<object>} */
|
|
80
|
+
const DataList = hostProvided("DataList");
|
|
81
|
+
/** @type {import('react').ComponentType<object>} */
|
|
82
|
+
const HexahChip = hostProvided("HexahChip");
|
|
83
|
+
/** @type {import('react').ComponentType<object>} */
|
|
84
|
+
const HexahPagination = hostProvided("HexahPagination");
|
|
85
|
+
/** @type {import('react').ComponentType<object>} */
|
|
86
|
+
const HexahPaginationItem = hostProvided("HexahPaginationItem");
|
|
87
|
+
|
|
88
|
+
/* ---------- Nawigacja / utile (host) ---------- */
|
|
89
|
+
/** @returns {string} */
|
|
90
|
+
const buildReportListHref = hostProvided("buildReportListHref");
|
|
91
|
+
/** @returns {string} */
|
|
92
|
+
const buildReportDetailHref = hostProvided("buildReportDetailHref");
|
|
93
|
+
/** @returns {string} */
|
|
94
|
+
const buildReportBackHref = hostProvided("buildReportBackHref");
|
|
95
|
+
/** @returns {number} */
|
|
96
|
+
const parseReportListPage = hostProvided("parseReportListPage");
|
|
97
|
+
/** @returns {'all'|'open'|'closed'} */
|
|
98
|
+
const parseReportStateFilter = hostProvided("parseReportStateFilter");
|
|
99
|
+
/** @returns {string} */
|
|
100
|
+
const parseReportTypeFilter = hostProvided("parseReportTypeFilter");
|
|
101
|
+
/** @returns {boolean} */
|
|
102
|
+
const parseReportMineFilter = hostProvided("parseReportMineFilter");
|
|
103
|
+
/** @returns {boolean} */
|
|
104
|
+
const parseReportPlannedFilter = hostProvided("parseReportPlannedFilter");
|
|
105
|
+
/** @returns {object} sx dla chipa stanu zgłoszenia */
|
|
106
|
+
const getStateChipSx = hostProvided("getStateChipSx");
|
|
107
|
+
/** @returns {object} sx dla chipa typu zgłoszenia */
|
|
108
|
+
const getIssueTypeChipSx = hostProvided("getIssueTypeChipSx");
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
useShard,
|
|
112
|
+
useShardUser,
|
|
113
|
+
useCharacter,
|
|
114
|
+
useAccount,
|
|
115
|
+
usePageData,
|
|
116
|
+
useOnlineCharacters,
|
|
117
|
+
useSnackbar,
|
|
118
|
+
useDialog,
|
|
119
|
+
useReports,
|
|
120
|
+
useGamePageShell,
|
|
121
|
+
SlotsProvider,
|
|
122
|
+
useSlots,
|
|
123
|
+
PageBox,
|
|
124
|
+
HexahCharacterSection,
|
|
125
|
+
AngularPanel,
|
|
126
|
+
StandardButton,
|
|
127
|
+
RoundAvatar,
|
|
128
|
+
DataList,
|
|
129
|
+
HexahChip,
|
|
130
|
+
HexahPagination,
|
|
131
|
+
HexahPaginationItem,
|
|
132
|
+
buildReportListHref,
|
|
133
|
+
buildReportDetailHref,
|
|
134
|
+
buildReportBackHref,
|
|
135
|
+
parseReportListPage,
|
|
136
|
+
parseReportStateFilter,
|
|
137
|
+
parseReportTypeFilter,
|
|
138
|
+
parseReportMineFilter,
|
|
139
|
+
parseReportPlannedFilter,
|
|
140
|
+
getStateChipSx,
|
|
141
|
+
getIssueTypeChipSx,
|
|
142
|
+
};
|