@artisan-cloud/plugin-framework-admin 0.0.1-alpha → 0.0.2-alpha

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/index.ts CHANGED
@@ -4,13 +4,27 @@ export interface PowerXAdminConfigOptions {
4
4
  }
5
5
 
6
6
  export function definePowerXAdminConfig(options: PowerXAdminConfigOptions) {
7
+ const pluginId = options.pluginId
8
+ const inferredApiBase =
9
+ process.env.NUXT_PUBLIC_POWERX_API_BASE?.trim() || `/_p/${pluginId}/api/v1`
10
+ const inferredCapabilityEndpoint =
11
+ process.env.NUXT_PUBLIC_POWERX_CAPABILITY_ENDPOINT?.trim() || '/integration/capabilities/invoke'
12
+
7
13
  return {
8
14
  extends: ['@artisan-cloud/plugin-framework-admin/layer'],
9
15
  appConfig: {
10
16
  powerx: {
11
- pluginId: options.pluginId,
17
+ pluginId,
12
18
  starterPages: options.starterPages ?? true
13
19
  }
20
+ },
21
+ runtimeConfig: {
22
+ public: {
23
+ powerx: {
24
+ apiBase: inferredApiBase,
25
+ capabilityEndpoint: inferredCapabilityEndpoint
26
+ }
27
+ }
14
28
  }
15
29
  }
16
30
  }
@@ -0,0 +1,40 @@
1
+ import { useNuxtApp } from '#app'
2
+ import type {
3
+ PowerXCapabilityBridge,
4
+ PowerXCapabilityRequest,
5
+ PowerXCapabilityResponse
6
+ } from '../plugins/powerx-capability.client'
7
+
8
+ export interface UsePowerXCapabilityOptions extends Partial<Omit<PowerXCapabilityRequest, 'capabilityId' | 'action' | 'payload'>> {
9
+ payload?: Record<string, any> | null
10
+ }
11
+
12
+ export type UsePowerXCapabilityInvoker = (
13
+ capabilityId: string,
14
+ action: string,
15
+ payload?: Record<string, any> | null,
16
+ options?: UsePowerXCapabilityOptions
17
+ ) => Promise<PowerXCapabilityResponse>
18
+
19
+ export const usePowerXCapability = (bridge?: PowerXCapabilityBridge): UsePowerXCapabilityInvoker => {
20
+ const nuxtApp = useNuxtApp()
21
+ const client = bridge ?? nuxtApp.$powerxCapability
22
+ if (!client) {
23
+ throw new Error('PowerX capability bridge is not initialized. Ensure the powerx-capability plugin is registered.')
24
+ }
25
+
26
+ return async (capabilityId, action, payload, options) => {
27
+ const mergedPayload = options?.payload ?? payload ?? {}
28
+
29
+ return client.invoke({
30
+ capabilityId,
31
+ action,
32
+ payload: mergedPayload,
33
+ headers: options?.headers,
34
+ requestId: options?.requestId,
35
+ signal: options?.signal,
36
+ apiBase: options?.apiBase,
37
+ endpoint: options?.endpoint
38
+ })
39
+ }
40
+ }
@@ -0,0 +1,142 @@
1
+ import { useRuntimeConfig } from '#imports'
2
+ import { ofetch, type OFetch, type FetchResponse } from 'ofetch'
3
+
4
+ const DEFAULT_ENDPOINT = '/integration/capabilities/invoke'
5
+
6
+ export interface PowerXCapabilityRequest {
7
+ capabilityId: string
8
+ action: string
9
+ payload?: Record<string, any> | null
10
+ headers?: Record<string, string>
11
+ requestId?: string
12
+ signal?: AbortSignal
13
+ apiBase?: string
14
+ endpoint?: string
15
+ }
16
+
17
+ export interface PowerXCapabilityResponse {
18
+ traceId?: string
19
+ status?: string
20
+ data?: Record<string, any> | null
21
+ errors?: Record<string, any> | Record<string, any>[] | null
22
+ }
23
+
24
+ export class PowerXCapabilityBridgeError extends Error {
25
+ status?: number
26
+ traceId?: string
27
+ details?: any
28
+
29
+ constructor(message: string, opts: { status?: number; traceId?: string; details?: any } = {}) {
30
+ super(message)
31
+ this.name = 'PowerXCapabilityBridgeError'
32
+ this.status = opts.status
33
+ this.traceId = opts.traceId
34
+ this.details = opts.details
35
+ }
36
+ }
37
+
38
+ export interface PowerXCapabilityBridge {
39
+ invoke(request: PowerXCapabilityRequest): Promise<PowerXCapabilityResponse>
40
+ }
41
+
42
+ interface BridgeOptions {
43
+ apiBase?: string
44
+ endpoint?: string
45
+ fetcher?: OFetch
46
+ }
47
+
48
+ const combineURL = (base?: string, endpoint?: string) => {
49
+ const normalizedBase = (base || '').replace(/\/+$/, '')
50
+ const normalizedEndpoint = ('/' + (endpoint || '').replace(/^\/+/, '')).replace(/\/{2,}/g, '/')
51
+ if (!normalizedBase) {
52
+ return normalizedEndpoint
53
+ }
54
+ return `${normalizedBase}${normalizedEndpoint}`
55
+ }
56
+
57
+ const createBridge = (options: BridgeOptions): PowerXCapabilityBridge => {
58
+ const fetcher = options.fetcher ?? ofetch
59
+ const endpoint = options.endpoint || DEFAULT_ENDPOINT
60
+ return {
61
+ async invoke(request) {
62
+ const capabilityId = request.capabilityId?.trim()
63
+ if (!capabilityId) {
64
+ throw new PowerXCapabilityBridgeError('capabilityId is required')
65
+ }
66
+ const action = request.action?.trim()
67
+ if (!action) {
68
+ throw new PowerXCapabilityBridgeError('action is required')
69
+ }
70
+ const url = combineURL(request.apiBase ?? options.apiBase, request.endpoint ?? endpoint)
71
+ const headers: Record<string, string> = {
72
+ 'Content-Type': 'application/json',
73
+ ...request.headers
74
+ }
75
+ if (request.requestId && !headers['X-Request-ID']) {
76
+ headers['X-Request-ID'] = request.requestId
77
+ }
78
+ let response: FetchResponse<Record<string, any>>
79
+ try {
80
+ response = await fetcher.raw(url, {
81
+ method: 'POST',
82
+ headers,
83
+ body: {
84
+ capabilityId,
85
+ action,
86
+ payload: request.payload ?? {}
87
+ },
88
+ signal: request.signal
89
+ })
90
+ } catch (error) {
91
+ throw new PowerXCapabilityBridgeError(error instanceof Error ? error.message : 'capability invoke failed')
92
+ }
93
+
94
+ const payload = response._data ?? {}
95
+ const traceId = payload.traceId || response.headers.get('x-trace-id') || undefined
96
+ if (response.status >= 400 || payload.error) {
97
+ throw new PowerXCapabilityBridgeError(payload.error?.message || 'capability invoke failed', {
98
+ status: response.status,
99
+ traceId,
100
+ details: payload.error || payload.errors
101
+ })
102
+ }
103
+
104
+ return {
105
+ traceId,
106
+ status: payload.status,
107
+ data: payload.data ?? null,
108
+ errors: payload.errors ?? null
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ export default defineNuxtPlugin((nuxtApp) => {
115
+ const runtimeConfig = useRuntimeConfig()
116
+ const publicConfig = runtimeConfig?.public as Record<string, any> | undefined
117
+ const powerxConfig = (publicConfig?.powerx ?? {}) as Record<string, any>
118
+
119
+ const bridge = createBridge({
120
+ apiBase: (powerxConfig.apiBase as string | undefined) ?? '',
121
+ endpoint: (powerxConfig.capabilityEndpoint as string | undefined) ?? DEFAULT_ENDPOINT,
122
+ fetcher: nuxtApp.$fetch
123
+ })
124
+
125
+ return {
126
+ provide: {
127
+ powerxCapability: bridge
128
+ }
129
+ }
130
+ })
131
+
132
+ declare module '#app' {
133
+ interface NuxtApp {
134
+ $powerxCapability: PowerXCapabilityBridge
135
+ }
136
+ }
137
+
138
+ declare module 'vue' {
139
+ interface ComponentCustomProperties {
140
+ $powerxCapability: PowerXCapabilityBridge
141
+ }
142
+ }
@@ -6,6 +6,14 @@ const layerDir = dirname(fileURLToPath(import.meta.url))
6
6
  const appDir = resolve(layerDir, 'app')
7
7
 
8
8
  const config: NuxtConfig = {
9
+ runtimeConfig: {
10
+ public: {
11
+ powerx: {
12
+ apiBase: process.env.NUXT_PUBLIC_POWERX_API_BASE ?? '',
13
+ capabilityEndpoint: process.env.NUXT_PUBLIC_POWERX_CAPABILITY_ENDPOINT ?? '/integration/capabilities/invoke'
14
+ }
15
+ }
16
+ },
9
17
  imports: {
10
18
  dirs: [
11
19
  resolve(appDir, 'composables'),
@@ -35,3 +43,12 @@ const config: NuxtConfig = {
35
43
  }
36
44
 
37
45
  export default config
46
+
47
+ declare module 'nuxt/schema' {
48
+ interface PublicRuntimeConfig {
49
+ powerx: {
50
+ apiBase: string
51
+ capabilityEndpoint: string
52
+ }
53
+ }
54
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artisan-cloud/plugin-framework-admin",
3
- "version": "0.0.1-alpha",
3
+ "version": "0.0.2-alpha",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts",