UNPKG

@scenemesh/entity-engine

Version:

一个“元数据驱动 + 组件适配 + 动态关系 + 视图管线”式的实体引擎。以 **Model + View + FieldType + SuiteAdapter + DataSource** 为五大支点,统一 CRUD / 查询 / 引用管理 / 视图渲染 / 扩展注册,支持在运行期无侵入拼装出 **表单、网格、主从、看板、仪表盘、流程/树形视图** 等多形态界面。

510 lines (496 loc) 16 kB
"use client"; import { BuildinModule, EntityActionRegistry, EntityComponentRegistry, EntityDataSourceFactory, EntityEngine, EntityEngineProvider, EntityEngineThemeProvider, EntityEventRegistry, EntityFieldDelegate, EntityMetaRegistry, EntityModelDelegate, EntityNamedRenderer, EntityPermissionGuard, EntitySessionManager, EntitySuiteAdapterProvider, EntityView, EntityViewContainer, EntityViewDelegate, EntityViewFieldDelegate, EntityWidget, EntityWidgetRenderer, ModelFieldTyperRegistry, TRPCEntityObjectDataSource, ViewContainerProvider, entityEngineDefaultTheme, getEntityEngine, toDataSourceHook, useContainerRouter, useEntityEngine, useEntityEngineRouter, useEntityEngineTheme, useEntityPermissionGuard, useEntitySession, useEntitySuiteAdapter, useMasterDetailViewContainer } from "./chunk-2MQEKMBU.mjs"; // src/core/types/session.types.ts var EntitySession = class { constructor(sessionId, userInfo, update = () => { }) { this.sessionId = sessionId; this.userInfo = userInfo; this.update = update; } isAuthenticated() { return !!this.userInfo; } }; // src/components/renderers/view-inspector/index.tsx import React2 from "react"; import { modals } from "@mantine/modals"; import JsonView from "@uiw/react-json-view"; import { Tabs, ActionIcon } from "@mantine/core"; import { ViewIcon, DatabaseIcon, Settings2Icon } from "lucide-react"; // src/lib/hooks/utils/use-async/use-async.ts import { useState, useDeferredValue } from "react"; // src/lib/hooks/utils/use-async-effect/use-async-effect.ts import { useRef, useEffect } from "react"; function useAsyncEffect(effect, deps) { const abortRef = useRef(null); useEffect(() => { abortRef.current = new AbortController(); return () => { abortRef.current?.abort(); }; }, []); useEffect(() => { let canceled = false; let result; const load = async () => { const signal = abortRef.current.signal; if (canceled || signal.aborted) { return; } result = await effect(signal); }; queueMicrotask(load); return () => { canceled = true; if (result) { result(); } }; }, deps); } // src/lib/hooks/utils/use-async/use-async.ts function useAsync(func, deps) { const [{ state, data, error }, setState] = useState({}); useAsyncEffect(async () => { setState((draft) => ({ ...draft, state: "loading" })); try { const result = await func(); setState({ state: "hasData", data: result }); } catch (e) { setState((draft) => ({ ...draft, state: "hasError", error: e })); } }, deps); return { state: state ?? "loading", data, error }; } function useAsyncWithCache(func, deps) { const [internalState, setInternalState] = useState({ state: "loading" }); const deferredResult = useDeferredValue(internalState); const [loadingState, setLoadingState] = useState(false); useAsyncEffect(async () => { setLoadingState(true); try { const result = await func(); setInternalState({ state: "hasData", data: result, error: void 0 }); } catch (e) { setInternalState({ state: "hasError", data: void 0, error: e }); } finally { setLoadingState(false); } }, deps); return { state: loadingState ? "loading" : deferredResult.state ?? "loading", data: deferredResult.data, error: deferredResult.error }; } // src/services/api/trpc/react.tsx import * as React from "react"; import SuperJSON2 from "superjson"; import { createTRPCReact } from "@trpc/react-query"; import { loggerLink, httpBatchStreamLink } from "@trpc/client"; import { QueryClientProvider } from "@tanstack/react-query"; // src/services/api/trpc/query-client.ts import SuperJSON from "superjson"; import { QueryClient, defaultShouldDehydrateQuery } from "@tanstack/react-query"; var createQueryClient = () => new QueryClient({ defaultOptions: { queries: { // With SSR, we usually want to set some default staleTime // above 0 to avoid refetching immediately on the client staleTime: 30 * 1e3 }, dehydrate: { serializeData: SuperJSON.serialize, shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending" }, hydrate: { deserializeData: SuperJSON.deserialize } } }); // src/services/api/trpc/react.tsx import { jsx } from "react/jsx-runtime"; var clientQueryClientSingleton = void 0; var getQueryClient = () => { if (typeof window === "undefined") { return createQueryClient(); } clientQueryClientSingleton ?? (clientQueryClientSingleton = createQueryClient()); return clientQueryClientSingleton; }; var api = createTRPCReact(); function TRPCReactProvider(props) { const engine = useEntityEngine(); const queryClient = getQueryClient(); const [trpcClient] = React.useState( () => api.createClient({ links: [ loggerLink({ enabled: (op) => false // process.env.NODE_ENV === 'development' || // (op.direction === 'down' && op.result instanceof Error), }), httpBatchStreamLink({ transformer: SuperJSON2, url: engine.settings.getUrl("/trpc"), ///api/ee/trpc headers: () => { const headers = new Headers(); headers.set("x-trpc-source", "nextjs-react"); return headers; } }) ] }) ); return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(api.Provider, { client: trpcClient, queryClient, children: props.children }) }); } // src/core/engine/engine.initializer.ts var EngineInitializer = class { constructor(args) { this._config = args.config; this._suiteAdapters = args.suiteAdapters; this._renderers = args.renderers; this._fieldTypers = args.fieldTypers; this._settings = args.settings; this._modules = args.modules; this._sessionProvider = args.sessionProvider; } async init(engine) { if (this._config) { const { models, views } = this._config; if (models.length > 0) { for (const model of models) { engine.metaRegistry.registerModel(model); } } if (views.length > 0) { for (const view of views) { engine.metaRegistry.registerView(view); } } } if (this._suiteAdapters && this._suiteAdapters.length > 0) { for (const adapter of this._suiteAdapters) { engine.componentRegistry.registerAdapter(adapter); } } if (this._renderers && this._renderers.length > 0) { for (const renderer of this._renderers) { engine.componentRegistry.registerRenderer(renderer); } } if (this._fieldTypers && this._fieldTypers.length > 0) { for (const typer of this._fieldTypers) { engine.fieldTyperRegistry.registerFieldTyper(typer); } } if (this._modules && this._modules.length > 0) { for (const mod of this._modules) { await engine.moduleRegistry.registerModule(mod, true); } } if (this._sessionProvider) { engine.sessionManager.setProvider(this._sessionProvider); } if (this._settings) { engine.settings.setBaseUrl(this._settings.baseUrl || ""); engine.settings.setEndpoint( this._settings.endpoint || process.env.EE_SERVICE_ROOT_PATH || "/api/ee" ); engine.settings.authenticationEnabled = this._settings.authenticationEnabled || false; } console.log( `Entity Engine initialized with models(${this._config?.models.length || 0}) and views(${this._config?.views.length || 0})` ); } }; // src/uikit/provider/entity-engine-provider-factory.tsx import { jsx as jsx2 } from "react/jsx-runtime"; function createEntityEngineProvider(config) { process.env.EE_SERVICE_ROOT_PATH = config.settings?.endpoint || "/api/ee"; const initializer = new EngineInitializer({ config: config.config, suiteAdapters: config.suiteAdapters, renderers: config.renderers, fieldTypers: config.fieldTypers, modules: config.modules, settings: config.settings }); const ConfiguredProvider = ({ children }) => /* @__PURE__ */ jsx2( EntitySuiteAdapterProvider, { adapter: config.suiteAdapter || { suiteName: "build-in", suiteVersion: "1.0.0" }, children: /* @__PURE__ */ jsx2( EntityEngineProvider, { initializer, loading: config.loading, router: config.router, permissionGuard: config.permissionGuard, children: /* @__PURE__ */ jsx2(EntityEngineThemeProvider, { theme: config.theme, children: /* @__PURE__ */ jsx2(TRPCReactProvider, { children }) }) } ) } ); ConfiguredProvider.displayName = "EntityEngineProvider(Configured)"; return ConfiguredProvider; } // src/uikit/surface/empty-symbol-comp.tsx function EmptySymbol() { return null; } // src/uikit/consumer/entity-object-consumer.tsx import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime"; function EntityObjectsConsumer(props) { const { modelName, query, withReference, objectsRenderer, onError, loading } = props; const engine = useEntityEngine(); const model = engine.metaRegistry.getModel(modelName); if (!model) { return null; } const datasource = engine.datasourceFactory.getDataSource(); const dsHook = toDataSourceHook(datasource); const { data, loading: isLoading, error } = dsHook.useFindMany({ modelName, query, withAllReferences: withReference || false }); if (isLoading || !data) { return loading || /* @__PURE__ */ jsx3("div", { children: "Loading..." }); } if (error) { if (onError) { return onError(error); } return /* @__PURE__ */ jsxs("div", { style: { color: "red" }, children: [ "Error: ", error.message ] }); } return /* @__PURE__ */ jsx3(Fragment, { children: objectsRenderer(data.count, data.data) }); } function EntityObjectConsumer(props) { const { modelName, objectId, withReference, objectRenderer, onError, loading } = props; const engine = useEntityEngine(); const datasource = engine.datasourceFactory.getDataSource(); const dsHook = toDataSourceHook(datasource); const { data, error, state } = useAsync(async () => { if (withReference) { return await datasource.findOneWithReferences({ modelName, id: objectId, includeFieldNames: void 0 }); } else { return await datasource.findOne({ modelName, id: objectId }); } }, [objectId, withReference]); if (state === "loading" || !data) { return loading || /* @__PURE__ */ jsx3("div", { children: "Loading..." }); } if (error) { if (onError) { return onError(error); } return /* @__PURE__ */ jsxs("div", { style: { color: "red" }, children: [ "Error: ", error.message ] }); } return /* @__PURE__ */ jsx3(Fragment, { children: objectRenderer(data) }); } // src/components/renderers/view-inspector/index.tsx import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime"; var EntityViewInspector = { name: "buildin-view-inspector", slotName: "view-inspector", disabled: false, renderer: (props) => /* @__PURE__ */ jsx4(EntityViewInspectorComp, { ...props }) }; function EntityViewInspectorComp(props) { const { model, viewData } = props; const handleAction = () => { modals.open({ title: "\u89C6\u56FE\u914D\u7F6E\u6570\u636E", children: /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(InnerEntityViewInspectorComp, { ...props, model, viewData }) }), size: "80%", centered: true, closeOnClickOutside: true, closeOnEscape: true, onClose: () => { console.log("Settings modal closed"); } }); }; return /* @__PURE__ */ jsx4(ActionIcon, { variant: "subtle", "aria-label": "Settings", onClick: handleAction, children: /* @__PURE__ */ jsx4(Settings2Icon, { size: 14, strokeWidth: 2 }) }); } function InnerEntityViewInspectorComp(props) { const { model, viewData, ...otherProps } = props; const [tab, setTab] = React2.useState("model"); const engine = useEntityEngine(); const handleTabChange = (value) => { setTab(value || "model"); }; return /* @__PURE__ */ jsxs2(Tabs, { value: tab, variant: "outline", color: "blue", radius: "md", onChange: handleTabChange, children: [ /* @__PURE__ */ jsxs2(Tabs.List, { children: [ /* @__PURE__ */ jsx4(Tabs.Tab, { value: "model", leftSection: /* @__PURE__ */ jsx4(ViewIcon, { size: 14, strokeWidth: 2 }), children: "\u6A21\u578B\u914D\u7F6E" }), /* @__PURE__ */ jsx4(Tabs.Tab, { value: "view", leftSection: /* @__PURE__ */ jsx4(DatabaseIcon, { size: 14, strokeWidth: 2 }), children: "\u89C6\u56FE\u914D\u7F6E" }), /* @__PURE__ */ jsx4(Tabs.Tab, { value: "other", leftSection: /* @__PURE__ */ jsx4(Settings2Icon, { size: 14, strokeWidth: 2 }), children: "\u5176\u4ED6\u6570\u636E" }) ] }), /* @__PURE__ */ jsx4(Tabs.Panel, { value: "model", pt: "xs", children: /* @__PURE__ */ jsx4( JsonView, { value: JSON.parse( JSON.stringify(model, (key, value) => { if (typeof key === "string" && key.startsWith("_metaRegistry")) { return void 0; } return value; }) ) } ) }), /* @__PURE__ */ jsx4(Tabs.Panel, { value: "view", pt: "xs", children: /* @__PURE__ */ jsx4( JsonView, { value: JSON.parse( JSON.stringify(viewData, (key, value) => { if (typeof key === "string" && key.startsWith("_metaRegistry")) { return void 0; } return value; }) ) } ) }), /* @__PURE__ */ jsx4(Tabs.Panel, { value: "other", pt: "xs", children: /* @__PURE__ */ jsx4( JsonView, { value: JSON.parse( JSON.stringify(otherProps, (key, value) => { if (typeof key === "string" && key.startsWith("_metaRegistry")) { return void 0; } return value; }) ) } ) }) ] }); } // src/types/data.types.ts var QueryOperator = /* @__PURE__ */ ((QueryOperator2) => { QueryOperator2["NONE"] = "none"; QueryOperator2["EQ"] = "eq"; QueryOperator2["NE"] = "ne"; QueryOperator2["GT"] = "gt"; QueryOperator2["GTE"] = "gte"; QueryOperator2["LT"] = "lt"; QueryOperator2["LTE"] = "lte"; QueryOperator2["CONTAINS"] = "contains"; QueryOperator2["STARTS_WITH"] = "startsWith"; QueryOperator2["ENDS_WITH"] = "endsWith"; QueryOperator2["IN"] = "in"; QueryOperator2["NOT_IN"] = "notIn"; QueryOperator2["IS_NULL"] = "isNull"; QueryOperator2["IS_NOT_NULL"] = "isNotNull"; QueryOperator2["BETWEEN"] = "between"; return QueryOperator2; })(QueryOperator || {}); export { BuildinModule, EmptySymbol, EntityActionRegistry, EntityComponentRegistry, EntityDataSourceFactory, EntityEngine, EntityEngineProvider, EntityEngineThemeProvider, EntityEventRegistry, EntityFieldDelegate, EntityMetaRegistry, EntityModelDelegate, EntityNamedRenderer, EntityObjectConsumer, EntityObjectsConsumer, EntityPermissionGuard, EntitySession, EntitySessionManager, EntitySuiteAdapterProvider, EntityView, EntityViewContainer, EntityViewDelegate, EntityViewFieldDelegate, EntityViewInspector, EntityWidget, EntityWidgetRenderer, ModelFieldTyperRegistry, QueryOperator, TRPCEntityObjectDataSource, ViewContainerProvider, createEntityEngineProvider, entityEngineDefaultTheme, getEntityEngine, toDataSourceHook, useAsync, useAsyncEffect, useAsyncWithCache, useContainerRouter, useEntityEngine, useEntityEngineRouter, useEntityEngineTheme, useEntityPermissionGuard, useEntitySession, useEntitySuiteAdapter, useMasterDetailViewContainer }; //# sourceMappingURL=index.mjs.map