@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 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
+ }
@@ -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
+ };