@fragmentsx/runtime-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # @fragmentsx/runtime-react
2
+
3
+ React-рантайм для встраивания опубликованных фрагментов Fragments в
4
+ приложение потребителя. Строится поверх `@fragmentsx/fragments-client` —
5
+ берёт готовый клиент и раскладывает его в провайдеры, хуки и компоненты
6
+ React.
7
+
8
+ ## Что это решает
9
+
10
+ Скомпилированный сервером фрагмент — это ESM-артефакт, который нужно
11
+ загрузить, смонтировать в дерево React, подключить его стили и дать доступу
12
+ к метрикам и переменным области. `runtime-react` закрывает весь этот путь:
13
+
14
+ - `FragmentsProvider` + `FragmentsClientProvider` — контекст клиента на
15
+ дерево приложения;
16
+ - `Area` / `FragmentRenderer` — резолвинг и рендер области/фрагмента;
17
+ - `useFragmentComponent`, `useFragmentStylesheet`, `FragmentStylesheet` —
18
+ подгрузка компонента и его CSS;
19
+ - `useFragmentMetrics`, `useArea`, `useProjectProperty` — доступ к метрикам,
20
+ данным области и project-переменным площадки внутри фрагмента.
21
+
22
+ ## Установка
23
+
24
+ ```bash
25
+ pnpm add @fragmentsx/runtime-react @fragmentsx/fragments-client react react-dom
26
+ ```
27
+
28
+ ## Использование
29
+
30
+ ```tsx
31
+ import { createBrowserClient, FragmentsProvider, Area } from '@fragmentsx/runtime-react';
32
+
33
+ const client = createBrowserClient({
34
+ apiUrl: 'https://api.fragments.example.com',
35
+ projectId: 'my-project',
36
+ });
37
+
38
+ function App() {
39
+ return (
40
+ <FragmentsProvider client={client}>
41
+ <Area code="home" />
42
+ </FragmentsProvider>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Пара с fragments-client
48
+
49
+ `runtime-react` зависит от `@fragmentsx/fragments-client` как от транспортного
50
+ ядра (`workspace:*` внутри монорепозитория, `^0.1.0` из npm) — устанавливать
51
+ и обновлять пакеты стоит вместе.
@@ -0,0 +1,290 @@
1
+ import { ComponentType } from 'react';
2
+ import { Context } from 'react';
3
+ import { createFragmentsClient } from '../../fragments-client/src/index.ts';
4
+ import { CreateFragmentsClientOptions } from '../../fragments-client/src/index.ts';
5
+ import { FC } from 'react';
6
+ import { FragmentsApiVersionError } from '../../fragments-client/src/index.ts';
7
+ import { FragmentsAreaError } from '../../fragments-client/src/index.ts';
8
+ import { FragmentsLoadError } from '../../fragments-client/src/index.ts';
9
+ import { GraphState } from '@graph-state/core';
10
+ import { JSX } from 'react/jsx-runtime';
11
+ import { ReactElement } from 'react';
12
+ import { ReactNode } from 'react';
13
+
14
+ /**
15
+ * Area — публичный компонент для рендеринга compiled fragment в consumer app.
16
+ *
17
+ * Req #4: Area + useArea оба public exports из @fragmentsx/runtime-react.
18
+ * Req #9: требует FragmentsProvider в tree (FragmentsClientContext + ModulesMapContext).
19
+ */
20
+ export declare const Area: FC<AreaProps>;
21
+
22
+ /**
23
+ * Props для Area компонента (D-09, сокращён Phase 73 B-13):
24
+ * - areaCode — строковый код области (Area.code в backend schema)
25
+ * - options.pause — пропустить рендер (conditional skip per legacy contract)
26
+ * - options.timeout — network timeout для $area.resolve fetch
27
+ * - options.ssr — отключить auto-resolve в SSR environment (default `true`);
28
+ * в browser auto-resolve запускается всегда. Параллель к render-react
29
+ * Area options.ssr.
30
+ */
31
+ export declare interface AreaProps {
32
+ areaCode: string;
33
+ options?: {
34
+ timeout?: number;
35
+ pause?: boolean;
36
+ ssr?: boolean;
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Browser-specific options — identical к CreateFragmentsClientOptions, но без `inject`.
42
+ * `inject` pre-populated через `injectStylesheet` из runtime-react автоматически.
43
+ */
44
+ declare type BrowserClientOptions = Omit<CreateFragmentsClientOptions, "inject">;
45
+
46
+ /**
47
+ * Создаёт FragmentsClient с pre-populated inject = injectStylesheet (BLOCKER-4-wiring bridge).
48
+ *
49
+ * @param opts - все поля CreateFragmentsClientOptions кроме inject
50
+ * @returns FragmentsClient готовый к использованию в browser environments
51
+ */
52
+ export declare function createBrowserClient(opts: BrowserClientOptions): ReturnType<typeof createFragmentsClient>;
53
+
54
+ /**
55
+ * Дефолтное значение, когда Provider не установлен (потребитель вне
56
+ * `FragmentRenderer`, например изолированный юнит-тест компонента).
57
+ */
58
+ export declare const defaultProjectProperties: Record<string, unknown>;
59
+
60
+ export declare interface FragmentMetrics {
61
+ reachGoal: (info: ReachGoalInfo) => void;
62
+ }
63
+
64
+ /**
65
+ * Публичный компонент — тело композиции за собственными Suspense + границей
66
+ * ошибок. `Area` тело не использует напрямую (у неё Suspense/граница уже
67
+ * стоят вокруг `useArea`); preview и прочие потребители без `areaCode`
68
+ * используют этот компонент целиком.
69
+ */
70
+ export declare const FragmentRenderer: FC<FragmentRendererProps>;
71
+
72
+ /**
73
+ * Контракт тела композиции (D-03). `globalCssUrl` — то, что сегодня отдаёт
74
+ * `useArea`; у preview (без области) это будет статика DRAFT-манифеста.
75
+ * `props` — единственный канал значений (Phase 73, B-02/B-06): готовые
76
+ * значения props корневого артефакта, разрешённые сервером либо переданные
77
+ * потребителем напрямую (preview).
78
+ */
79
+ export declare interface FragmentRendererProps {
80
+ fragmentId: number;
81
+ props?: Record<string, unknown>;
82
+ globalCssUrl?: string | null;
83
+ /** Словарь project-переменных площадки (манифест области или блок данных preview); ключ — имя переменной, значение — голое (D-72-02). */
84
+ projectProperties?: Record<string, unknown>;
85
+ onError?: (error: Error) => void;
86
+ }
87
+
88
+ export { FragmentsApiVersionError }
89
+
90
+ export { FragmentsAreaError }
91
+
92
+ /**
93
+ * Псевдоним для GraphState — возвращаемый тип `createFragmentsClient(...)` из
94
+ * `@fragmentsx/client-core`. Compiled Fragment.tsx не зависит от client-core
95
+ * напрямую: он импортирует тип отсюда и читает client через
96
+ * `FragmentsClientContext` (D-07).
97
+ */
98
+ export declare type FragmentsClient = GraphState;
99
+
100
+ /**
101
+ * Single source of truth для FragmentsClient в compiled output.
102
+ *
103
+ * Значение `null` означает «client ещё не установлен» — defensive в
104
+ * `useFragmentMetrics` чтобы reachGoal был no-op (preview / SSG-первая загрузка
105
+ * до hydration).
106
+ */
107
+ export declare const FragmentsClientContext: Context<FragmentsClient | null>;
108
+
109
+ /**
110
+ * React-компонент-обёртка для установки FragmentsClient в Context. Эмиссия
111
+ * compiled-output её не вызывает — это app-level wiring entry point.
112
+ */
113
+ export declare function FragmentsClientProvider(props: {
114
+ value: FragmentsClient;
115
+ children: ReactNode;
116
+ }): JSX.Element;
117
+
118
+ export { FragmentsLoadError }
119
+
120
+ /**
121
+ * Unified Provider для compiled fragments.
122
+ *
123
+ * Одновременно устанавливает:
124
+ * - FragmentsClientContext: client — для useArea cache lookup + $metrics.reachGoal
125
+ * - ModulesMapContext: {read} cache-reader — для useFragmentComponent delegation
126
+ *
127
+ * useMemo критично — Context value identity stability (без него cache invalidation
128
+ * на каждый render).
129
+ */
130
+ export declare function FragmentsProvider({ client, children }: FragmentsProviderProps): ReactElement;
131
+
132
+ declare interface FragmentsProviderProps {
133
+ client: FragmentsClient;
134
+ children: ReactNode;
135
+ }
136
+
137
+ export declare function FragmentStylesheet({ href, fragmentKey }: FragmentStylesheetProps): JSX.Element | null;
138
+
139
+ declare interface FragmentStylesheetProps {
140
+ href: string;
141
+ fragmentKey: string;
142
+ }
143
+
144
+ /**
145
+ * Инжектирует `<link rel="stylesheet">` в `document.head` с ref-count dedup.
146
+ *
147
+ * @param href - CDN URL стилевого файла
148
+ * @param key - уникальный ключ для dedup (например `chunk:201` или `fragment:42:abc`)
149
+ * @returns cleanup callback — при refCount = 0 удаляет `<link>` из head
150
+ */
151
+ export declare const injectStylesheet: (href: string, key: string) => (() => void);
152
+
153
+ export declare const InstanceIdContext: Context<string | null>;
154
+
155
+ /**
156
+ * React Context для инъекции cache-reader'а в дерево компонентов.
157
+ * Null default = «provider не установлен» (no-provider signal).
158
+ */
159
+ export declare const ModulesMapContext: Context<ModulesMapContextValue | null>;
160
+
161
+ /**
162
+ * Значение ModulesMapContext. Единственная fn — cache-reader (D-05).
163
+ *
164
+ * @param id - числовой идентификатор фрагмента
165
+ * @returns ComponentType (cache hit) | throws Promise (pending) | throws FragmentsLoadError (missing)
166
+ */
167
+ export declare interface ModulesMapContextValue {
168
+ read: (id: number) => ComponentType<any>;
169
+ }
170
+
171
+ /**
172
+ * React-компонент-обёртка для установки ModulesMapContext в дерево.
173
+ * Value identity stability — ответственность caller'а (FragmentsProvider обернёт
174
+ * `{read: (id) => client.$fragments.read(id)}` в useMemo, Plan 08).
175
+ *
176
+ * @param value - cache-reader объект (D-05)
177
+ * @param children - дочерние компоненты
178
+ */
179
+ export declare function ModulesMapProvider({ value, children, }: ModulesMapProviderProps): ReactElement;
180
+
181
+ /**
182
+ * Props для ModulesMapProvider.
183
+ * Caller (FragmentsProvider) передаёт useMemo'нутый объект value.
184
+ * Для unit-тестов: raw {read: mockFn} без useMemo (Req #9 standalone escape hatch).
185
+ */
186
+ declare interface ModulesMapProviderProps {
187
+ value: ModulesMapContextValue;
188
+ children: ReactNode;
189
+ }
190
+
191
+ export declare const ProjectPropertiesContext: Context<Record<string, unknown> | null>;
192
+
193
+ /**
194
+ * Payload для `reachGoal`. Phase 3 НЕ автоинъектит scope-поля
195
+ * (variantId / areaId / campaignId) — caller передаёт явно, типизация
196
+ * сообщает о доступных полях. Совпадает по shape с
197
+ * `client-core.metricsPlugin GoalInfo` (см. metrics/index.ts).
198
+ */
199
+ export declare interface ReachGoalInfo {
200
+ goalId: number;
201
+ variantId?: number;
202
+ areaId?: number;
203
+ campaignId?: number;
204
+ }
205
+
206
+ export declare function useArea(areaCode: string, options?: UseAreaOptions): UseAreaResult;
207
+
208
+ /**
209
+ * Опции хука. По дизайну API параллелен render-react useArea:
210
+ * - `timeout` — fetch timeout (передаётся в `$area.resolve`).
211
+ * - `ssr` — стартовать ли auto-resolve на сервере (default `true`, чтобы
212
+ * Next.js getServerSideProps мог hydration'ить с прогретым кэшем).
213
+ * В browser environment auto-resolve всегда включён.
214
+ */
215
+ declare interface UseAreaOptions {
216
+ timeout?: number;
217
+ ssr?: boolean;
218
+ }
219
+
220
+ /**
221
+ * Result shape возвращаемый useArea — зеркалирует AreaResolveResult из client-core.
222
+ */
223
+ declare interface UseAreaResult {
224
+ rootFragmentId: number;
225
+ /**
226
+ * Phase 73, B-02/B-06: значения props корневого артефакта, разрешённые сервером
227
+ * (`resolveVariantProps`) — привязки `$ref` сюда не доезжают, только готовые значения.
228
+ * `$area` уже нормализует отсутствие поля у backend'а в `{}`, поэтому поле
229
+ * обязательное — потребителю не нужна повторная optional-проверка.
230
+ */
231
+ props: Record<string, unknown>;
232
+ /**
233
+ * Phase 72 D-72-01/D-72-02: словарь project-переменных площадки, шейкнутый по
234
+ * факту использования — вход для `useProjectProperty` из `@fragmentsx/runtime-react`.
235
+ * `$area` уже нормализует отсутствие поля у backend'а в `{}` (план 72-05,
236
+ * редукция), поэтому поле обязательное — повторная optional-проверка
237
+ * потребителю не нужна.
238
+ */
239
+ projectProperties: Record<string, unknown>;
240
+ /**
241
+ * URL к глобальному baseline reset CSS ([data-key^="Text"], [data-key^="Fragment"]).
242
+ * `null` — backend не отдал global reset; baseline применяться не будет.
243
+ */
244
+ globalCssUrl: string | null;
245
+ }
246
+
247
+ /**
248
+ * useCssChunk — no-op stub для compiled fragments.
249
+ *
250
+ * @deprecated TODO(runtime-react): удалить вместе с compiler emission.
251
+ *
252
+ * Контекст: fragment-compiler (Phase 4 Plan 04-03) эмитит
253
+ * `import { useCssChunk } from "@fragmentsx/runtime-react"` + lifted calls
254
+ * `useCssChunk(<chunkId>);` в Fragment init body — отражая runtime-контракт
255
+ * interpreter'а (`packages/render-react/src/nodes/CssChunk/hooks/useCssChunk.ts`).
256
+ *
257
+ * В compiled production-pipeline CSS chunks инжектятся раньше — на уровне
258
+ * `$area.resolve → $cssChunks.populate → injectStylesheet` (см.
259
+ * `packages/client-core/src/compiled/$area/index.ts:186-187` и
260
+ * `packages/client-core/src/compiled/$cssChunks/index.ts:74-86`). К моменту mount
261
+ * fragment'а `<link rel="stylesheet">` уже в DOM (с ref-count dedup в injectStylesheet
262
+ * registry). Per-fragment hook не делает дополнительной работы.
263
+ *
264
+ * Будущая работа (удаление stub'а):
265
+ * 1. Compiler: убрать lifted `useCssChunk` emission из composeComponent.ts
266
+ * (cssChunks discovery остаётся для $cssChunks.populate manifest gen на backend).
267
+ * 2. Runtime: удалить этот файл и его export из index.ts.
268
+ * 3. Альтернатива (если потребуется per-fragment ref-count cleanup) — заменить
269
+ * stub на useEffect с ref-count в $cssChunks для proper unmount cleanup.
270
+ *
271
+ * Открыто как deferred item в `.planning/workstreams/fragment-compiler` —
272
+ * "runtime-react: убрать useCssChunk stub после compiler emission cleanup".
273
+ */
274
+ export declare function useCssChunk(_chunkId: number): void;
275
+
276
+ export declare function useFragmentComponent(fragmentId: number): ComponentType<any>;
277
+
278
+ /**
279
+ * Возвращает обёртку над client.$metrics.reachGoal.
280
+ *
281
+ * - Без `<FragmentsClientProvider>` — reachGoal no-op (не бросает).
282
+ * - С provider — делегирует на `client.$metrics.reachGoal(info)`.
283
+ */
284
+ export declare function useFragmentMetrics(): FragmentMetrics;
285
+
286
+ export declare function useFragmentStylesheet(href: string, key: string): void;
287
+
288
+ export declare function useProjectProperty(name: string): unknown;
289
+
290
+ export { }
@@ -0,0 +1,202 @@
1
+ import { jsx as o, jsxs as M, Fragment as j } from "react/jsx-runtime";
2
+ import { createContext as f, useContext as a, useMemo as g, useLayoutEffect as N, useEffect as S, version as I, Component as R, Suspense as C } from "react";
3
+ import { createFragmentsClient as k } from "@fragmentsx/fragments-client";
4
+ import { FragmentsApiVersionError as ne, FragmentsAreaError as oe, FragmentsLoadError as se } from "@fragmentsx/fragments-client";
5
+ const c = f(
6
+ null
7
+ );
8
+ c.displayName = "FragmentsClientContext";
9
+ function _(r) {
10
+ return /* @__PURE__ */ o(c.Provider, { value: r.value, children: r.children });
11
+ }
12
+ function b() {
13
+ return a(c);
14
+ }
15
+ const v = f(null);
16
+ v.displayName = "InstanceIdContext";
17
+ const h = f(
18
+ null
19
+ );
20
+ h.displayName = "ModulesMapContext";
21
+ function B({
22
+ value: r,
23
+ children: e
24
+ }) {
25
+ return /* @__PURE__ */ o(h.Provider, { value: r, children: e });
26
+ }
27
+ function H() {
28
+ const r = a(c);
29
+ return g(
30
+ () => ({
31
+ reachGoal: (e) => {
32
+ var n, t;
33
+ (t = (n = r == null ? void 0 : r.$metrics) == null ? void 0 : n.reachGoal) == null || t.call(n, e);
34
+ }
35
+ }),
36
+ [r]
37
+ );
38
+ }
39
+ function L(r) {
40
+ const e = a(h);
41
+ if (!e)
42
+ throw new Error("[useFragmentComponent] No ModulesMapProvider in tree");
43
+ return e.read(r);
44
+ }
45
+ const U = typeof window < "u";
46
+ function $(r, e) {
47
+ const n = a(c);
48
+ if (!n)
49
+ throw new Error("[useArea] No FragmentsClientProvider in tree");
50
+ const t = n.$area;
51
+ if (!(t != null && t.read))
52
+ throw new Error("[useArea] $area plugin not mounted");
53
+ if ((U || ((e == null ? void 0 : e.ssr) ?? !0)) && !t.has(r)) {
54
+ const i = (e == null ? void 0 : e.timeout) != null ? { timeout: e.timeout } : void 0;
55
+ t.resolve(r, i);
56
+ }
57
+ return t.read(r);
58
+ }
59
+ const E = typeof window < "u", D = () => {
60
+ }, d = /* @__PURE__ */ new Map(), F = (r, e) => {
61
+ if (!E)
62
+ return D;
63
+ const n = d.get(e);
64
+ if (n)
65
+ return n.refCount += 1, () => {
66
+ n.refCount -= 1, n.refCount === 0 && (n.linkEl.remove(), d.delete(e));
67
+ };
68
+ const t = document.createElement("link");
69
+ t.rel = "stylesheet", t.href = r, t.setAttribute("data-fragment-key", e), document.head.appendChild(t);
70
+ const s = { refCount: 1, linkEl: t };
71
+ return d.set(e, s), () => {
72
+ s.refCount -= 1, s.refCount === 0 && (s.linkEl.remove(), d.delete(e));
73
+ };
74
+ }, O = E ? N : S;
75
+ function V(r, e) {
76
+ O(() => F(r, e), [r, e]);
77
+ }
78
+ const G = parseInt(I.split(".")[0], 10), K = G >= 19;
79
+ function m({ href: r, fragmentKey: e }) {
80
+ return K ? /* @__PURE__ */ o(
81
+ "link",
82
+ {
83
+ rel: "stylesheet",
84
+ href: r,
85
+ precedence: "default",
86
+ "data-fragment-key": e
87
+ }
88
+ ) : (V(r, e), null);
89
+ }
90
+ function Q(r) {
91
+ }
92
+ const P = {}, p = f(null);
93
+ p.displayName = "ProjectPropertiesContext";
94
+ class y extends R {
95
+ constructor() {
96
+ super(...arguments), this.state = { hasError: !1 };
97
+ }
98
+ static getDerivedStateFromError() {
99
+ return { hasError: !0 };
100
+ }
101
+ componentDidCatch(e, n) {
102
+ var t, s;
103
+ process.env.NODE_ENV !== "production" && console.error("[fragments] Area error:", e, n.componentStack), (s = (t = this.props).onError) == null || s.call(t, e);
104
+ }
105
+ render() {
106
+ return this.state.hasError ? null : this.props.children;
107
+ }
108
+ }
109
+ const x = ({
110
+ fragmentId: r,
111
+ props: e,
112
+ globalCssUrl: n = null,
113
+ projectProperties: t = P
114
+ }) => {
115
+ const s = L(r), l = b().$fragments, u = l == null ? void 0 : l.getRef(r), w = l == null ? void 0 : l.getScopeClass(r), A = e ?? {};
116
+ return /* @__PURE__ */ M(j, { children: [
117
+ n != null && /* @__PURE__ */ o(
118
+ m,
119
+ {
120
+ href: n,
121
+ fragmentKey: "global"
122
+ }
123
+ ),
124
+ u && /* @__PURE__ */ o(
125
+ m,
126
+ {
127
+ href: u.cssUrl,
128
+ fragmentKey: `fragment:${r}:css`
129
+ }
130
+ ),
131
+ (u == null ? void 0 : u.fontsUrl) != null && /* @__PURE__ */ o(
132
+ m,
133
+ {
134
+ href: u.fontsUrl,
135
+ fragmentKey: `fragment:${r}:fonts`
136
+ }
137
+ ),
138
+ /* @__PURE__ */ o(p.Provider, { value: t, children: /* @__PURE__ */ o(v.Provider, { value: null, children: /* @__PURE__ */ o(
139
+ "div",
140
+ {
141
+ className: w,
142
+ style: { display: "contents" },
143
+ children: /* @__PURE__ */ o(s, { ...A })
144
+ }
145
+ ) }) })
146
+ ] });
147
+ }, W = ({ onError: r, ...e }) => /* @__PURE__ */ o(C, { fallback: null, children: /* @__PURE__ */ o(y, { onError: r, children: /* @__PURE__ */ o(x, { ...e }) }) }), T = ({ areaCode: r, options: e }) => {
148
+ if ((e == null ? void 0 : e.pause) === !0)
149
+ return null;
150
+ const { rootFragmentId: n, props: t, projectProperties: s, globalCssUrl: i } = $(r, {
151
+ timeout: e == null ? void 0 : e.timeout,
152
+ ssr: e == null ? void 0 : e.ssr
153
+ });
154
+ return /* @__PURE__ */ o(
155
+ x,
156
+ {
157
+ fragmentId: n,
158
+ props: t,
159
+ globalCssUrl: i,
160
+ projectProperties: s
161
+ }
162
+ );
163
+ }, X = ({ areaCode: r, options: e }) => /* @__PURE__ */ o(C, { fallback: null, children: /* @__PURE__ */ o(y, { children: /* @__PURE__ */ o(T, { areaCode: r, options: e }) }) });
164
+ function Y({ client: r, children: e }) {
165
+ const n = g(
166
+ () => ({
167
+ read: (t) => r.$fragments.read(t)
168
+ }),
169
+ [r]
170
+ );
171
+ return /* @__PURE__ */ o(_, { value: r, children: /* @__PURE__ */ o(B, { value: n, children: e }) });
172
+ }
173
+ function Z(r) {
174
+ return (a(p) ?? P)[r];
175
+ }
176
+ function ee(r) {
177
+ return k({ ...r, inject: F });
178
+ }
179
+ export {
180
+ X as Area,
181
+ W as FragmentRenderer,
182
+ m as FragmentStylesheet,
183
+ ne as FragmentsApiVersionError,
184
+ oe as FragmentsAreaError,
185
+ c as FragmentsClientContext,
186
+ _ as FragmentsClientProvider,
187
+ se as FragmentsLoadError,
188
+ Y as FragmentsProvider,
189
+ v as InstanceIdContext,
190
+ h as ModulesMapContext,
191
+ B as ModulesMapProvider,
192
+ p as ProjectPropertiesContext,
193
+ ee as createBrowserClient,
194
+ P as defaultProjectProperties,
195
+ F as injectStylesheet,
196
+ $ as useArea,
197
+ Q as useCssChunk,
198
+ L as useFragmentComponent,
199
+ H as useFragmentMetrics,
200
+ V as useFragmentStylesheet,
201
+ Z as useProjectProperty
202
+ };
@@ -0,0 +1 @@
1
+ (function(t,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("react"),require("@fragmentsx/fragments-client")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","@fragmentsx/fragments-client"],n):(t=typeof globalThis<"u"?globalThis:t||self,n(t.FragmentsRuntimeReact={},t.jsxRuntime,t.React,t.fragmentsClient))})(this,function(t,n,u,d){"use strict";const a=u.createContext(null);a.displayName="FragmentsClientContext";function y(r){return n.jsx(a.Provider,{value:r.value,children:r.children})}function b(){return u.useContext(a)}const F=u.createContext(null);F.displayName="InstanceIdContext";const f=u.createContext(null);f.displayName="ModulesMapContext";function E({value:r,children:e}){return n.jsx(f.Provider,{value:r,children:e})}function x(){const r=u.useContext(a);return u.useMemo(()=>({reachGoal:e=>{var o,s;(s=(o=r==null?void 0:r.$metrics)==null?void 0:o.reachGoal)==null||s.call(o,e)}}),[r])}function p(r){const e=u.useContext(f);if(!e)throw new Error("[useFragmentComponent] No ModulesMapProvider in tree");return e.read(r)}const I=typeof window<"u";function M(r,e){const o=u.useContext(a);if(!o)throw new Error("[useArea] No FragmentsClientProvider in tree");const s=o.$area;if(!(s!=null&&s.read))throw new Error("[useArea] $area plugin not mounted");if((I||((e==null?void 0:e.ssr)??!0))&&!s.has(r)){const h=(e==null?void 0:e.timeout)!=null?{timeout:e.timeout}:void 0;s.resolve(r,h)}return s.read(r)}const j=typeof window<"u",N=()=>{},m=new Map,P=(r,e)=>{if(!j)return N;const o=m.get(e);if(o)return o.refCount+=1,()=>{o.refCount-=1,o.refCount===0&&(o.linkEl.remove(),m.delete(e))};const s=document.createElement("link");s.rel="stylesheet",s.href=r,s.setAttribute("data-fragment-key",e),document.head.appendChild(s);const l={refCount:1,linkEl:s};return m.set(e,l),()=>{l.refCount-=1,l.refCount===0&&(l.linkEl.remove(),m.delete(e))}},k=j?u.useLayoutEffect:u.useEffect;function A(r,e){k(()=>P(r,e),[r,e])}const O=parseInt(u.version.split(".")[0],10)>=19;function g({href:r,fragmentKey:e}){return O?n.jsx("link",{rel:"stylesheet",href:r,precedence:"default","data-fragment-key":e}):(A(r,e),null)}function _(r){}const v={},C=u.createContext(null);C.displayName="ProjectPropertiesContext";class w extends u.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,o){var s,l;process.env.NODE_ENV!=="production"&&console.error("[fragments] Area error:",e,o.componentStack),(l=(s=this.props).onError)==null||l.call(s,e)}render(){return this.state.hasError?null:this.props.children}}const S=({fragmentId:r,props:e,globalCssUrl:o=null,projectProperties:s=v})=>{const l=p(r),c=b().$fragments,i=c==null?void 0:c.getRef(r),q=c==null?void 0:c.getScopeClass(r),D=e??{};return n.jsxs(n.Fragment,{children:[o!=null&&n.jsx(g,{href:o,fragmentKey:"global"}),i&&n.jsx(g,{href:i.cssUrl,fragmentKey:`fragment:${r}:css`}),(i==null?void 0:i.fontsUrl)!=null&&n.jsx(g,{href:i.fontsUrl,fragmentKey:`fragment:${r}:fonts`}),n.jsx(C.Provider,{value:s,children:n.jsx(F.Provider,{value:null,children:n.jsx("div",{className:q,style:{display:"contents"},children:n.jsx(l,{...D})})})})]})},T=({onError:r,...e})=>n.jsx(u.Suspense,{fallback:null,children:n.jsx(w,{onError:r,children:n.jsx(S,{...e})})}),B=({areaCode:r,options:e})=>{if((e==null?void 0:e.pause)===!0)return null;const{rootFragmentId:o,props:s,projectProperties:l,globalCssUrl:h}=M(r,{timeout:e==null?void 0:e.timeout,ssr:e==null?void 0:e.ssr});return n.jsx(S,{fragmentId:o,props:s,globalCssUrl:h,projectProperties:l})},L=({areaCode:r,options:e})=>n.jsx(u.Suspense,{fallback:null,children:n.jsx(w,{children:n.jsx(B,{areaCode:r,options:e})})});function U({client:r,children:e}){const o=u.useMemo(()=>({read:s=>r.$fragments.read(s)}),[r]);return n.jsx(y,{value:r,children:n.jsx(E,{value:o,children:e})})}function V(r){return(u.useContext(C)??v)[r]}function $(r){return d.createFragmentsClient({...r,inject:P})}Object.defineProperty(t,"FragmentsApiVersionError",{enumerable:!0,get:()=>d.FragmentsApiVersionError}),Object.defineProperty(t,"FragmentsAreaError",{enumerable:!0,get:()=>d.FragmentsAreaError}),Object.defineProperty(t,"FragmentsLoadError",{enumerable:!0,get:()=>d.FragmentsLoadError}),t.Area=L,t.FragmentRenderer=T,t.FragmentStylesheet=g,t.FragmentsClientContext=a,t.FragmentsClientProvider=y,t.FragmentsProvider=U,t.InstanceIdContext=F,t.ModulesMapContext=f,t.ModulesMapProvider=E,t.ProjectPropertiesContext=C,t.createBrowserClient=$,t.defaultProjectProperties=v,t.injectStylesheet=P,t.useArea=M,t.useCssChunk=_,t.useFragmentComponent=p,t.useFragmentMetrics=x,t.useFragmentStylesheet=A,t.useProjectProperty=V,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@fragmentsx/runtime-react",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/FireworksX/fragments.git",
9
+ "directory": "packages/runtime-react"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "main": "./dist/index.umd.js",
20
+ "module": "./dist/index.es.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.es.js",
26
+ "require": "./dist/index.umd.js",
27
+ "source": "./src/index.ts"
28
+ }
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@graph-state/core": "^0.13.3",
35
+ "@fragmentsx/fragments-client": "0.1.0"
36
+ },
37
+ "devDependencies": {
38
+ "@testing-library/jest-dom": "^6.6.3",
39
+ "@testing-library/react": "^16.0.1",
40
+ "@types/react": "^19.0.0",
41
+ "@vitejs/plugin-react": "^4.3.4",
42
+ "jsdom": "^27.4.0",
43
+ "react": "^19.0.0",
44
+ "react-dom": "^19.2.3",
45
+ "typescript": "^5.5.4",
46
+ "vite": "^6.1.0",
47
+ "vite-plugin-dts": "^4.5.0",
48
+ "vitest": "^3.1.1"
49
+ },
50
+ "scripts": {
51
+ "clear": "rm -r -f ./dist",
52
+ "build": "vite build",
53
+ "dev": "vite build -w",
54
+ "test": "vitest run --config ./vitest.config.ts",
55
+ "test:watch": "vitest --config ./vitest.config.ts",
56
+ "lint": "eslint . --ext .ts,.tsx --fix"
57
+ }
58
+ }