@notur/sdk 1.0.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.
Files changed (41) hide show
  1. package/dist/createExtension.d.ts +21 -0
  2. package/dist/createExtension.d.ts.map +1 -0
  3. package/dist/createExtension.js +65 -0
  4. package/dist/createExtension.js.map +1 -0
  5. package/dist/hooks/useExtensionConfig.d.ts +17 -0
  6. package/dist/hooks/useExtensionConfig.d.ts.map +1 -0
  7. package/dist/hooks/useExtensionConfig.js +94 -0
  8. package/dist/hooks/useExtensionConfig.js.map +1 -0
  9. package/dist/hooks/useNavigate.d.ts +12 -0
  10. package/dist/hooks/useNavigate.d.ts.map +1 -0
  11. package/dist/hooks/useNavigate.js +19 -0
  12. package/dist/hooks/useNavigate.js.map +1 -0
  13. package/dist/hooks/useNoturEvent.d.ts +10 -0
  14. package/dist/hooks/useNoturEvent.d.ts.map +1 -0
  15. package/dist/hooks/useNoturEvent.js +23 -0
  16. package/dist/hooks/useNoturEvent.js.map +1 -0
  17. package/dist/hooks/usePermission.d.ts +5 -0
  18. package/dist/hooks/usePermission.d.ts.map +1 -0
  19. package/dist/hooks/usePermission.js +25 -0
  20. package/dist/hooks/usePermission.js.map +1 -0
  21. package/dist/hooks/useServerContext.d.ts +15 -0
  22. package/dist/hooks/useServerContext.d.ts.map +1 -0
  23. package/dist/hooks/useServerContext.js +31 -0
  24. package/dist/hooks/useServerContext.js.map +1 -0
  25. package/dist/hooks/useUserContext.d.ts +12 -0
  26. package/dist/hooks/useUserContext.d.ts.map +1 -0
  27. package/dist/hooks/useUserContext.js +38 -0
  28. package/dist/hooks/useUserContext.js.map +1 -0
  29. package/dist/index.d.ts +10 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +9 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/types.d.ts +146 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/types.js +11 -0
  36. package/dist/types.js.map +1 -0
  37. package/package.json +43 -0
  38. package/templates/ExampleComponent.tsx.template +40 -0
  39. package/templates/ServiceProvider.php.template +54 -0
  40. package/templates/extension.yaml.template +43 -0
  41. package/webpack.extension.config.js +52 -0
@@ -0,0 +1,21 @@
1
+ import { ExtensionDefinition } from './types';
2
+ /**
3
+ * Factory for registering a Notur extension.
4
+ *
5
+ * Usage:
6
+ * ```ts
7
+ * import { createExtension } from '@notur/sdk';
8
+ *
9
+ * createExtension({
10
+ * config: { id: 'acme/analytics', name: 'Analytics', version: '1.0.0' },
11
+ * slots: [
12
+ * { slot: 'dashboard.widgets', component: AnalyticsWidget, order: 10 },
13
+ * ],
14
+ * routes: [
15
+ * { area: 'server', path: '/analytics', name: 'Analytics', component: AnalyticsPage },
16
+ * ],
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function createExtension(definition: ExtensionDefinition): void;
21
+ //# sourceMappingURL=createExtension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createExtension.d.ts","sourceRoot":"","sources":["../src/createExtension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAe,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CA+BrE"}
@@ -0,0 +1,65 @@
1
+ import { getNoturApi } from './types';
2
+ /**
3
+ * Factory for registering a Notur extension.
4
+ *
5
+ * Usage:
6
+ * ```ts
7
+ * import { createExtension } from '@notur/sdk';
8
+ *
9
+ * createExtension({
10
+ * config: { id: 'acme/analytics', name: 'Analytics', version: '1.0.0' },
11
+ * slots: [
12
+ * { slot: 'dashboard.widgets', component: AnalyticsWidget, order: 10 },
13
+ * ],
14
+ * routes: [
15
+ * { area: 'server', path: '/analytics', name: 'Analytics', component: AnalyticsPage },
16
+ * ],
17
+ * });
18
+ * ```
19
+ */
20
+ export function createExtension(definition) {
21
+ const { config, slots = [], routes = [], onInit, onDestroy, cssIsolation } = definition;
22
+ const api = getNoturApi();
23
+ const resolvedCssIsolation = resolveCssIsolation(api, config.id, cssIsolation);
24
+ // Register the extension
25
+ api.registry.registerExtension({
26
+ id: config.id,
27
+ name: config.name,
28
+ version: config.version,
29
+ slots: slots.map(s => ({ ...s, extensionId: config.id })),
30
+ routes: routes.map(r => ({ ...r, extensionId: config.id })),
31
+ cssIsolation: resolvedCssIsolation,
32
+ });
33
+ // Register destroy callback
34
+ if (onDestroy) {
35
+ api.registry.registerDestroyCallback(config.id, onDestroy);
36
+ }
37
+ // Run init callback
38
+ if (onInit) {
39
+ try {
40
+ onInit();
41
+ }
42
+ catch (e) {
43
+ console.error(`[Notur] Extension ${config.id} init error:`, e);
44
+ }
45
+ }
46
+ console.log(`[Notur] Extension registered: ${config.id} v${config.version}`);
47
+ }
48
+ function resolveCssIsolation(api, extensionId, local) {
49
+ var _a, _b;
50
+ if (local === true) {
51
+ return { mode: 'root-class' };
52
+ }
53
+ if (local && typeof local === 'object') {
54
+ return local;
55
+ }
56
+ const manifestConfig = (_b = (_a = api.extensions) === null || _a === void 0 ? void 0 : _a.find(ext => ext.id === extensionId)) === null || _b === void 0 ? void 0 : _b.cssIsolation;
57
+ if (manifestConfig && typeof manifestConfig === 'object') {
58
+ if ('class' in manifestConfig && !manifestConfig.className) {
59
+ return { ...manifestConfig, className: manifestConfig.class };
60
+ }
61
+ return manifestConfig;
62
+ }
63
+ return undefined;
64
+ }
65
+ //# sourceMappingURL=createExtension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createExtension.js","sourceRoot":"","sources":["../src/createExtension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAAC,UAA+B;IAC3D,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,UAAU,CAAC;IACxF,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAE1B,MAAM,oBAAoB,GAAG,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAE/E,yBAAyB;IACzB,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC3B,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3D,YAAY,EAAE,oBAAoB;KACrC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,IAAI,SAAS,EAAE,CAAC;QACZ,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED,oBAAoB;IACpB,IAAI,MAAM,EAAE,CAAC;QACT,IAAI,CAAC;YACD,MAAM,EAAE,CAAC;QACb,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,qBAAqB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACnE,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,mBAAmB,CACxB,GAAmC,EACnC,WAAmB,EACnB,KAA2C;;IAE3C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,YAAqB,EAAE,CAAC;IAC3C,CAAC;IAED,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,cAAc,GAAG,MAAA,MAAA,GAAG,CAAC,UAAU,0CAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,WAAW,CAAC,0CAAE,YAAY,CAAC;IACzF,IAAI,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;QACvD,IAAI,OAAO,IAAK,cAAsB,IAAI,CAAE,cAAsB,CAAC,SAAS,EAAE,CAAC;YAC3E,OAAO,EAAE,GAAG,cAAc,EAAE,SAAS,EAAG,cAAsB,CAAC,KAAK,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,cAAc,CAAC;IAC1B,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC"}
@@ -0,0 +1,17 @@
1
+ interface UseExtensionConfigOptions<T> {
2
+ baseUrl?: string;
3
+ initial?: T;
4
+ pollInterval?: number;
5
+ }
6
+ interface ExtensionConfigState<T> {
7
+ config: T;
8
+ loading: boolean;
9
+ error: string | null;
10
+ refresh: () => Promise<T>;
11
+ }
12
+ /**
13
+ * Fetch public extension settings exposed via admin.settings.*.public.
14
+ */
15
+ export declare function useExtensionConfig<T extends Record<string, any> = Record<string, any>>(extensionId: string, options?: UseExtensionConfigOptions<T>): ExtensionConfigState<T>;
16
+ export {};
17
+ //# sourceMappingURL=useExtensionConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useExtensionConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/useExtensionConfig.ts"],"names":[],"mappings":"AAEA,UAAU,yBAAyB,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,oBAAoB,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,yBAAyB,CAAC,CAAC,CAAM,GAC3C,oBAAoB,CAAC,CAAC,CAAC,CAoGzB"}
@@ -0,0 +1,94 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ /**
3
+ * Fetch public extension settings exposed via admin.settings.*.public.
4
+ */
5
+ export function useExtensionConfig(extensionId, options = {}) {
6
+ const { baseUrl, initial, pollInterval } = options;
7
+ const [config, setConfig] = useState((initial !== null && initial !== void 0 ? initial : {}));
8
+ const [loading, setLoading] = useState(true);
9
+ const [error, setError] = useState(null);
10
+ const mountedRef = useRef(true);
11
+ useEffect(() => {
12
+ mountedRef.current = true;
13
+ return () => {
14
+ mountedRef.current = false;
15
+ };
16
+ }, []);
17
+ const refresh = useCallback(async () => {
18
+ var _a;
19
+ if (!extensionId) {
20
+ const empty = (initial !== null && initial !== void 0 ? initial : {});
21
+ if (mountedRef.current) {
22
+ setConfig(empty);
23
+ setLoading(false);
24
+ setError(null);
25
+ }
26
+ return empty;
27
+ }
28
+ if (mountedRef.current) {
29
+ setLoading(true);
30
+ setError(null);
31
+ }
32
+ const apiBase = baseUrl || '/api/client/notur';
33
+ const url = `${apiBase}/extensions/${extensionId}/settings`;
34
+ try {
35
+ const response = await fetch(url, {
36
+ method: 'GET',
37
+ headers: { Accept: 'application/json' },
38
+ credentials: 'same-origin',
39
+ });
40
+ if (!response.ok) {
41
+ let message = `Settings request failed: ${response.status} ${response.statusText}`;
42
+ try {
43
+ const body = await response.json();
44
+ if (body === null || body === void 0 ? void 0 : body.message) {
45
+ message = body.message;
46
+ }
47
+ }
48
+ catch (_b) {
49
+ // Ignore JSON parse failures
50
+ }
51
+ throw new Error(message);
52
+ }
53
+ const payload = await response.json();
54
+ const data = ((_a = payload === null || payload === void 0 ? void 0 : payload.data) !== null && _a !== void 0 ? _a : {});
55
+ if (mountedRef.current) {
56
+ setConfig(data);
57
+ setLoading(false);
58
+ setError(null);
59
+ }
60
+ return data;
61
+ }
62
+ catch (err) {
63
+ const message = (err === null || err === void 0 ? void 0 : err.message) || 'Failed to load settings.';
64
+ if (mountedRef.current) {
65
+ setLoading(false);
66
+ setError(message);
67
+ }
68
+ throw err;
69
+ }
70
+ }, [baseUrl, extensionId, initial]);
71
+ useEffect(() => {
72
+ refresh();
73
+ }, [refresh]);
74
+ useEffect(() => {
75
+ if (!pollInterval || pollInterval <= 0) {
76
+ return;
77
+ }
78
+ const id = window.setInterval(() => {
79
+ refresh().catch(() => {
80
+ // ignore polling errors; surface via hook state
81
+ });
82
+ }, pollInterval);
83
+ return () => {
84
+ window.clearInterval(id);
85
+ };
86
+ }, [pollInterval, refresh]);
87
+ return {
88
+ config,
89
+ loading,
90
+ error,
91
+ refresh,
92
+ };
93
+ }
94
+ //# sourceMappingURL=useExtensionConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useExtensionConfig.js","sourceRoot":"","sources":["../../src/hooks/useExtensionConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ interface UseNavigateOptions {
2
+ extensionId: string;
3
+ }
4
+ /**
5
+ * Returns a navigate function scoped to the extension's route namespace.
6
+ * Navigates to `/notur/{extensionId}/{path}` using pushState.
7
+ */
8
+ export declare function useNavigate({ extensionId }: UseNavigateOptions): (path: string, options?: {
9
+ replace?: boolean;
10
+ }) => void;
11
+ export {};
12
+ //# sourceMappingURL=useNavigate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavigate.d.ts","sourceRoot":"","sources":["../../src/hooks/useNavigate.ts"],"names":[],"mappings":"AAEA,UAAU,kBAAkB;IACxB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,WAAW,EAAE,EAAE,kBAAkB,UAEhD,MAAM,YAAY;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,UAYrD"}
@@ -0,0 +1,19 @@
1
+ import { useCallback } from 'react';
2
+ /**
3
+ * Returns a navigate function scoped to the extension's route namespace.
4
+ * Navigates to `/notur/{extensionId}/{path}` using pushState.
5
+ */
6
+ export function useNavigate({ extensionId }) {
7
+ return useCallback((path, options) => {
8
+ const fullPath = `/notur/${extensionId}${path.startsWith('/') ? path : '/' + path}`;
9
+ if (options === null || options === void 0 ? void 0 : options.replace) {
10
+ window.history.replaceState(null, '', fullPath);
11
+ }
12
+ else {
13
+ window.history.pushState(null, '', fullPath);
14
+ }
15
+ // Dispatch popstate so RouteRenderer picks up the change
16
+ window.dispatchEvent(new PopStateEvent('popstate'));
17
+ }, [extensionId]);
18
+ }
19
+ //# sourceMappingURL=useNavigate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavigate.js","sourceRoot":"","sources":["../../src/hooks/useNavigate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAMpC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,WAAW,EAAsB;IAC3D,OAAO,WAAW,CACd,CAAC,IAAY,EAAE,OAA+B,EAAE,EAAE;QAC9C,MAAM,QAAQ,GAAG,UAAU,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC;QACpF,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACJ,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QACjD,CAAC;QACD,yDAAyD;QACzD,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;IACxD,CAAC,EACD,CAAC,WAAW,CAAC,CAChB,CAAC;AACN,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Subscribe to an inter-extension event.
3
+ * Automatically unsubscribes on unmount.
4
+ */
5
+ export declare function useNoturEvent(event: string, handler: (data?: unknown) => void): void;
6
+ /**
7
+ * Returns a function to emit events on the inter-extension event bus.
8
+ */
9
+ export declare function useEmitEvent(): (event: string, data?: unknown) => void;
10
+ //# sourceMappingURL=useNoturEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNoturEvent.d.ts","sourceRoot":"","sources":["../../src/hooks/useNoturEvent.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAMpF;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAKtE"}
@@ -0,0 +1,23 @@
1
+ import { useEffect, useCallback } from 'react';
2
+ import { getNoturApi } from '../types';
3
+ /**
4
+ * Subscribe to an inter-extension event.
5
+ * Automatically unsubscribes on unmount.
6
+ */
7
+ export function useNoturEvent(event, handler) {
8
+ useEffect(() => {
9
+ const api = getNoturApi();
10
+ const unsubscribe = api.registry.onEvent(event, handler);
11
+ return unsubscribe;
12
+ }, [event, handler]);
13
+ }
14
+ /**
15
+ * Returns a function to emit events on the inter-extension event bus.
16
+ */
17
+ export function useEmitEvent() {
18
+ return useCallback((event, data) => {
19
+ const api = getNoturApi();
20
+ api.registry.emitEvent(event, data);
21
+ }, []);
22
+ }
23
+ //# sourceMappingURL=useNoturEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNoturEvent.js","sourceRoot":"","sources":["../../src/hooks/useNoturEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,OAAiC;IAC1E,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,WAAW,CAAC;IACvB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IACxB,OAAO,WAAW,CAAC,CAAC,KAAa,EAAE,IAAc,EAAE,EAAE;QACjD,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Hook to check if the current user has a specific extension permission.
3
+ */
4
+ export declare function usePermission(permission: string): boolean;
5
+ //# sourceMappingURL=usePermission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePermission.d.ts","sourceRoot":"","sources":["../../src/hooks/usePermission.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAwBzD"}
@@ -0,0 +1,25 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useServerContext } from './useServerContext';
3
+ /**
4
+ * Hook to check if the current user has a specific extension permission.
5
+ */
6
+ export function usePermission(permission) {
7
+ const serverContext = useServerContext();
8
+ const [hasPermission, setHasPermission] = useState(false);
9
+ useEffect(() => {
10
+ if (!serverContext) {
11
+ setHasPermission(false);
12
+ return;
13
+ }
14
+ // Admin override
15
+ if (serverContext.isOwner) {
16
+ setHasPermission(true);
17
+ return;
18
+ }
19
+ // Check permissions array
20
+ const perms = serverContext.permissions || [];
21
+ setHasPermission(perms.includes('*') || perms.includes(permission));
22
+ }, [serverContext, permission]);
23
+ return hasPermission;
24
+ }
25
+ //# sourceMappingURL=usePermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePermission.js","sourceRoot":"","sources":["../../src/hooks/usePermission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC5C,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1D,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,gBAAgB,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO;QACX,CAAC;QAED,iBAAiB;QACjB,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YACxB,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACvB,OAAO;QACX,CAAC;QAED,0BAA0B;QAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,WAAW,IAAI,EAAE,CAAC;QAC9C,gBAAgB,CACZ,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CACpD,CAAC;IACN,CAAC,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;IAEhC,OAAO,aAAa,CAAC;AACzB,CAAC"}
@@ -0,0 +1,15 @@
1
+ interface ServerContext {
2
+ uuid: string;
3
+ name: string;
4
+ node: string;
5
+ isOwner: boolean;
6
+ status: string | null;
7
+ permissions: string[];
8
+ }
9
+ /**
10
+ * Hook to access the current server context from the Pterodactyl panel.
11
+ * Only available within server-scoped pages.
12
+ */
13
+ export declare function useServerContext(): ServerContext | null;
14
+ export {};
15
+ //# sourceMappingURL=useServerContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useServerContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useServerContext.ts"],"names":[],"mappings":"AAEA,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CA0BvD"}
@@ -0,0 +1,31 @@
1
+ import { useState, useEffect } from 'react';
2
+ /**
3
+ * Hook to access the current server context from the Pterodactyl panel.
4
+ * Only available within server-scoped pages.
5
+ */
6
+ export function useServerContext() {
7
+ const [context, setContext] = useState(null);
8
+ useEffect(() => {
9
+ // Pterodactyl stores server data in the ServerContext React context
10
+ // We read it from the DOM data attribute as a fallback
11
+ const serverElement = document.getElementById('app');
12
+ if (!serverElement)
13
+ return;
14
+ const serverData = serverElement.dataset.server;
15
+ if (serverData) {
16
+ try {
17
+ setContext(JSON.parse(serverData));
18
+ }
19
+ catch (_a) {
20
+ // Not on a server page
21
+ }
22
+ }
23
+ // Also check the URL for server UUID
24
+ const match = window.location.pathname.match(/\/server\/([a-f0-9-]+)/);
25
+ if (match) {
26
+ setContext(prev => prev || { uuid: match[1], name: '', node: '', isOwner: false, status: null, permissions: [] });
27
+ }
28
+ }, []);
29
+ return context;
30
+ }
31
+ //# sourceMappingURL=useServerContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useServerContext.js","sourceRoot":"","sources":["../../src/hooks/useServerContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAW5C;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC5B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAuB,IAAI,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACX,oEAAoE;QACpE,uDAAuD;QACvD,MAAM,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC;QAChD,IAAI,UAAU,EAAE,CAAC;YACb,IAAI,CAAC;gBACD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACvC,CAAC;YAAC,WAAM,CAAC;gBACL,uBAAuB;YAC3B,CAAC;QACL,CAAC;QAED,qCAAqC;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACvE,IAAI,KAAK,EAAE,CAAC;YACR,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QACtH,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,OAAO,CAAC;AACnB,CAAC"}
@@ -0,0 +1,12 @@
1
+ interface UserContext {
2
+ uuid: string;
3
+ username: string;
4
+ email: string;
5
+ isAdmin: boolean;
6
+ }
7
+ /**
8
+ * Hook to access the current user context from the Pterodactyl panel.
9
+ */
10
+ export declare function useUserContext(): UserContext | null;
11
+ export {};
12
+ //# sourceMappingURL=useUserContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUserContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useUserContext.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAoCnD"}
@@ -0,0 +1,38 @@
1
+ import { useState, useEffect } from 'react';
2
+ /**
3
+ * Hook to access the current user context from the Pterodactyl panel.
4
+ */
5
+ export function useUserContext() {
6
+ const [user, setUser] = useState(null);
7
+ useEffect(() => {
8
+ let cancelled = false;
9
+ // Try reading from Pterodactyl's global state
10
+ const storeState = window.PterodactylUser;
11
+ if (storeState) {
12
+ setUser(storeState);
13
+ return;
14
+ }
15
+ // Fallback: fetch from API
16
+ fetch('/api/client/account', {
17
+ headers: { Accept: 'application/json' },
18
+ credentials: 'same-origin',
19
+ })
20
+ .then(r => r.json())
21
+ .then(data => {
22
+ if (cancelled)
23
+ return;
24
+ if (data.attributes) {
25
+ setUser({
26
+ uuid: data.attributes.uuid,
27
+ username: data.attributes.username,
28
+ email: data.attributes.email,
29
+ isAdmin: data.attributes.admin || false,
30
+ });
31
+ }
32
+ })
33
+ .catch(() => { });
34
+ return () => { cancelled = true; };
35
+ }, []);
36
+ return user;
37
+ }
38
+ //# sourceMappingURL=useUserContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUserContext.js","sourceRoot":"","sources":["../../src/hooks/useUserContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAS5C;;GAEG;AACH,MAAM,UAAU,cAAc;IAC1B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAC;IAE3D,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,8CAA8C;QAC9C,MAAM,UAAU,GAAI,MAAc,CAAC,eAAe,CAAC;QACnD,IAAI,UAAU,EAAE,CAAC;YACb,OAAO,CAAC,UAAU,CAAC,CAAC;YACpB,OAAO;QACX,CAAC;QAED,2BAA2B;QAC3B,KAAK,CAAC,qBAAqB,EAAE;YACzB,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,WAAW,EAAE,aAAa;SAC7B,CAAC;aACG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACnB,IAAI,CAAC,IAAI,CAAC,EAAE;YACT,IAAI,SAAS;gBAAE,OAAO;YACtB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,OAAO,CAAC;oBACJ,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI;oBAC1B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,QAAQ;oBAClC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK;oBAC5B,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,KAAK;iBAC1C,CAAC,CAAC;YACP,CAAC;QACL,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAErB,OAAO,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,IAAI,CAAC;AAChB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { createExtension } from './createExtension';
2
+ export { getNoturApi } from './types';
3
+ export type { ExtensionConfig, SlotConfig, RouteConfig, ExtensionDefinition, NoturApi, } from './types';
4
+ export { useServerContext } from './hooks/useServerContext';
5
+ export { useUserContext } from './hooks/useUserContext';
6
+ export { usePermission } from './hooks/usePermission';
7
+ export { useExtensionConfig } from './hooks/useExtensionConfig';
8
+ export { useNoturEvent, useEmitEvent } from './hooks/useNoturEvent';
9
+ export { useNavigate } from './hooks/useNavigate';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EACR,eAAe,EACf,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,QAAQ,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { createExtension } from './createExtension';
2
+ export { getNoturApi } from './types';
3
+ export { useServerContext } from './hooks/useServerContext';
4
+ export { useUserContext } from './hooks/useUserContext';
5
+ export { usePermission } from './hooks/usePermission';
6
+ export { useExtensionConfig } from './hooks/useExtensionConfig';
7
+ export { useNoturEvent, useEmitEvent } from './hooks/useNoturEvent';
8
+ export { useNavigate } from './hooks/useNavigate';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAQtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,146 @@
1
+ export interface ExtensionConfig {
2
+ id: string;
3
+ name: string;
4
+ version: string;
5
+ }
6
+ export interface CssIsolationConfig {
7
+ mode: 'root-class';
8
+ className?: string;
9
+ }
10
+ export interface SlotRenderContext {
11
+ path: string;
12
+ area: 'server' | 'dashboard' | 'account' | 'admin' | 'auth' | 'other';
13
+ isServer: boolean;
14
+ isDashboard: boolean;
15
+ isAccount: boolean;
16
+ isAdmin: boolean;
17
+ isAuth: boolean;
18
+ permissions: string[] | null;
19
+ }
20
+ export interface SlotRenderWhen {
21
+ area?: SlotRenderContext['area'];
22
+ areas?: Array<SlotRenderContext['area']>;
23
+ path?: string | string[];
24
+ pathStartsWith?: string | string[];
25
+ pathIncludes?: string | string[];
26
+ pathMatches?: string | RegExp;
27
+ permission?: string | string[];
28
+ server?: boolean;
29
+ dashboard?: boolean;
30
+ account?: boolean;
31
+ admin?: boolean;
32
+ auth?: boolean;
33
+ }
34
+ export type SlotRenderCondition = boolean | SlotRenderWhen | ((context: SlotRenderContext) => boolean);
35
+ export interface SlotConfig {
36
+ slot: string;
37
+ component: React.ComponentType<any>;
38
+ order?: number;
39
+ priority?: number;
40
+ label?: string;
41
+ icon?: string;
42
+ permission?: string;
43
+ props?: Record<string, any>;
44
+ when?: SlotRenderCondition;
45
+ }
46
+ export interface RouteConfig {
47
+ area: 'server' | 'dashboard' | 'account';
48
+ path: string;
49
+ name: string;
50
+ component: React.ComponentType<any>;
51
+ icon?: string;
52
+ permission?: string;
53
+ }
54
+ export interface ExtensionDefinition {
55
+ config: ExtensionConfig;
56
+ slots?: SlotConfig[];
57
+ routes?: RouteConfig[];
58
+ cssIsolation?: CssIsolationConfig | boolean;
59
+ onInit?: () => void;
60
+ onDestroy?: () => void;
61
+ }
62
+ export interface SlotRegistration {
63
+ extensionId: string;
64
+ slot: string;
65
+ component: React.ComponentType<any>;
66
+ order?: number;
67
+ priority?: number;
68
+ label?: string;
69
+ icon?: string;
70
+ permission?: string;
71
+ scopeClass?: string;
72
+ props?: Record<string, any>;
73
+ when?: SlotRenderCondition;
74
+ }
75
+ export interface RouteRegistration {
76
+ extensionId: string;
77
+ area: 'server' | 'dashboard' | 'account';
78
+ path: string;
79
+ name: string;
80
+ component: React.ComponentType<any>;
81
+ icon?: string;
82
+ permission?: string;
83
+ scopeClass?: string;
84
+ }
85
+ export interface NoturApi {
86
+ version: string;
87
+ registry: {
88
+ registerSlot: (registration: Omit<SlotRegistration, 'extensionId'> & {
89
+ extensionId?: string;
90
+ }) => void;
91
+ registerRoute: (area: RouteRegistration['area'], route: Omit<RouteRegistration, 'extensionId' | 'area'> & {
92
+ extensionId?: string;
93
+ }) => void;
94
+ registerExtension: (ext: {
95
+ id: string;
96
+ name: string;
97
+ version: string;
98
+ slots: SlotRegistration[];
99
+ routes: RouteRegistration[];
100
+ theme?: {
101
+ variables: Record<string, string>;
102
+ priority?: number;
103
+ };
104
+ cssIsolation?: CssIsolationConfig;
105
+ }) => void;
106
+ registerDestroyCallback: (extensionId: string, callback: () => void) => void;
107
+ unregisterExtension: (extensionId: string) => void;
108
+ getSlot: (slotId: string) => SlotRegistration[];
109
+ getRoutes: (area: RouteRegistration['area']) => RouteRegistration[];
110
+ on: (event: string, callback: () => void) => () => void;
111
+ emitEvent: (event: string, data?: unknown) => void;
112
+ onEvent: (event: string, callback: (data?: unknown) => void) => () => void;
113
+ };
114
+ hooks: {
115
+ useSlot: (slotId: string) => SlotRegistration[];
116
+ useExtensionApi: (options: {
117
+ extensionId: string;
118
+ baseUrl?: string;
119
+ }) => {
120
+ data: unknown;
121
+ loading: boolean;
122
+ error: string | null;
123
+ get: (path: string) => Promise<unknown>;
124
+ post: (path: string, body?: unknown) => Promise<unknown>;
125
+ put: (path: string, body?: unknown) => Promise<unknown>;
126
+ patch: (path: string, body?: unknown) => Promise<unknown>;
127
+ delete: (path: string) => Promise<unknown>;
128
+ request: (path: string, options?: RequestInit) => Promise<unknown>;
129
+ };
130
+ useExtensionState: <T extends Record<string, any>>(extensionId: string, initialState: T) => [T, (partial: Partial<T>) => void, () => void];
131
+ useNoturTheme: () => Record<string, string>;
132
+ };
133
+ SLOT_IDS: Record<string, string>;
134
+ unregisterExtension: (id: string) => void;
135
+ extensions?: Array<{
136
+ id: string;
137
+ bundle?: string;
138
+ styles?: string;
139
+ cssIsolation?: CssIsolationConfig;
140
+ }>;
141
+ }
142
+ /**
143
+ * Get the Notur API from the window global.
144
+ */
145
+ export declare function getNoturApi(): NoturApi;
146
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Get the Notur API from the window global.
3
+ */
4
+ export function getNoturApi() {
5
+ const api = window.__NOTUR__;
6
+ if (!api) {
7
+ throw new Error('[Notur SDK] Bridge runtime not found. Ensure bridge.js is loaded first.');
8
+ }
9
+ return api;
10
+ }
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAuIA;;GAEG;AACH,MAAM,UAAU,WAAW;IACvB,MAAM,GAAG,GAAI,MAAc,CAAC,SAAS,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@notur/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Notur Extension Developer SDK",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/sak0a/notur.git",
12
+ "directory": "sdk"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "keywords": [
18
+ "notur",
19
+ "pterodactyl",
20
+ "extension",
21
+ "plugin",
22
+ "sdk"
23
+ ],
24
+ "files": [
25
+ "dist/",
26
+ "webpack.extension.config.js",
27
+ "templates/"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "dev": "tsc --watch",
32
+ "prepublishOnly": "bun run build"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^16.14.0",
36
+ "react-dom": "^16.14.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.3.0",
40
+ "@types/react": "^16.14.0",
41
+ "@types/react-dom": "^16.9.0"
42
+ }
43
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { createExtension } from '@notur/sdk';
3
+
4
+ const ExampleWidget: React.FC<{ extensionId: string }> = ({ extensionId }) => {
5
+ return (
6
+ <div
7
+ style={{
8
+ padding: '1rem',
9
+ background: 'var(--notur-glass-bg, var(--notur-bg-secondary))',
10
+ border: '1px solid var(--notur-glass-border, var(--notur-border))',
11
+ borderRadius: 'var(--notur-radius-lg)',
12
+ boxShadow: 'var(--notur-glass-shadow, 0 12px 30px rgba(0, 0, 0, 0.35))',
13
+ backdropFilter: 'blur(var(--notur-glass-blur, 12px))',
14
+ WebkitBackdropFilter: 'blur(var(--notur-glass-blur, 12px))',
15
+ }}
16
+ >
17
+ <h3 style={{ color: 'var(--notur-text-primary)' }}>
18
+ {{displayName}}
19
+ </h3>
20
+ <p style={{ color: 'var(--notur-text-secondary)' }}>
21
+ Hello from {{vendor}}/{{name}}!
22
+ </p>
23
+ </div>
24
+ );
25
+ };
26
+
27
+ createExtension({
28
+ config: {
29
+ id: '{{vendor}}/{{name}}',
30
+ name: '{{displayName}}',
31
+ version: '1.0.0',
32
+ },
33
+ slots: [
34
+ {
35
+ slot: 'dashboard.widgets',
36
+ component: ExampleWidget,
37
+ order: 100,
38
+ },
39
+ ],
40
+ });
@@ -0,0 +1,54 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace {{namespace}};
6
+
7
+ use Notur\Contracts\ExtensionInterface;
8
+ use Notur\Contracts\HasRoutes;
9
+ use Notur\Contracts\HasFrontendSlots;
10
+
11
+ class {{className}} implements ExtensionInterface, HasRoutes, HasFrontendSlots
12
+ {
13
+ public function getId(): string
14
+ {
15
+ return '{{vendor}}/{{name}}';
16
+ }
17
+
18
+ public function getName(): string
19
+ {
20
+ return '{{displayName}}';
21
+ }
22
+
23
+ public function getVersion(): string
24
+ {
25
+ return '1.0.0';
26
+ }
27
+
28
+ public function register(): void
29
+ {
30
+ //
31
+ }
32
+
33
+ public function boot(): void
34
+ {
35
+ //
36
+ }
37
+
38
+ public function getBasePath(): string
39
+ {
40
+ return __DIR__ . '/..';
41
+ }
42
+
43
+ public function getRouteFiles(): array
44
+ {
45
+ return [
46
+ 'api-client' => 'src/routes/api-client.php',
47
+ ];
48
+ }
49
+
50
+ public function getFrontendSlots(): array
51
+ {
52
+ return [];
53
+ }
54
+ }
@@ -0,0 +1,43 @@
1
+ notur: "1.0"
2
+ id: "{{vendor}}/{{name}}"
3
+ name: "{{displayName}}"
4
+ version: "1.0.0"
5
+ description: "{{description}}"
6
+ authors:
7
+ - name: "{{authorName}}"
8
+ license: "MIT"
9
+
10
+ requires:
11
+ notur: "^1.0"
12
+ pterodactyl: "^1.11"
13
+ php: "^8.2"
14
+
15
+ capabilities:
16
+ routes: "^1"
17
+ health: "^1"
18
+ schedules: "^1"
19
+ css_isolation: "^1"
20
+
21
+ entrypoint: "{{namespace}}\\{{className}}"
22
+
23
+ autoload:
24
+ psr-4:
25
+ "{{namespace}}\\": "src/"
26
+
27
+ backend:
28
+ routes:
29
+ api-client: "src/routes/api-client.php"
30
+ migrations: "database/migrations"
31
+ permissions: []
32
+
33
+ frontend:
34
+ bundle: "resources/frontend/dist/extension.js"
35
+ css_isolation:
36
+ mode: "root-class"
37
+ slots: {}
38
+
39
+ health:
40
+ checks: []
41
+
42
+ schedules:
43
+ tasks: []
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Base webpack configuration for Notur extensions.
3
+ *
4
+ * Extension developers can import and extend this config:
5
+ *
6
+ * ```js
7
+ * const base = require('@notur/sdk/webpack.extension.config');
8
+ *
9
+ * module.exports = {
10
+ * ...base,
11
+ * output: {
12
+ * ...base.output,
13
+ * library: { name: '__NOTUR_EXT_MyExtension__', type: 'umd' },
14
+ * },
15
+ * };
16
+ * ```
17
+ */
18
+ const path = require('path');
19
+
20
+ module.exports = {
21
+ entry: './src/index.ts',
22
+ output: {
23
+ filename: 'bundle.js',
24
+ path: path.resolve(process.cwd(), 'dist'),
25
+ library: {
26
+ name: '__NOTUR_EXT__',
27
+ type: 'umd',
28
+ },
29
+ clean: true,
30
+ },
31
+ resolve: {
32
+ extensions: ['.ts', '.tsx', '.js', '.jsx'],
33
+ },
34
+ module: {
35
+ rules: [
36
+ {
37
+ test: /\.tsx?$/,
38
+ use: 'ts-loader',
39
+ exclude: /node_modules/,
40
+ },
41
+ {
42
+ test: /\.css$/,
43
+ use: ['style-loader', 'css-loader'],
44
+ },
45
+ ],
46
+ },
47
+ externals: {
48
+ react: 'React',
49
+ 'react-dom': 'ReactDOM',
50
+ '@notur/sdk': '__NOTUR__',
51
+ },
52
+ };