@peanut-admin/admin 0.1.0-alpha.11
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 +202 -0
- package/admin-core/src/access/access.ts +27 -0
- package/admin-core/src/access/permission-policy.ts +37 -0
- package/admin-core/src/api/client.ts +200 -0
- package/admin-core/src/api/problem.ts +67 -0
- package/admin-core/src/api/refresh.ts +122 -0
- package/admin-core/src/auth/stores.ts +121 -0
- package/admin-core/src/auth/tenant-session.ts +32 -0
- package/admin-core/src/generated/api.d.ts +22106 -0
- package/admin-core/src/governance/audit.ts +56 -0
- package/admin-core/src/governance/catalog.ts +86 -0
- package/admin-core/src/governance/index.ts +26 -0
- package/admin-core/src/governance/menu.ts +63 -0
- package/admin-core/src/governance/roles.ts +144 -0
- package/admin-core/src/governance/types.ts +64 -0
- package/admin-core/src/index.ts +122 -0
- package/admin-core/src/lifecycle/tenant.ts +59 -0
- package/admin-core/src/module/contribution.ts +124 -0
- package/admin-core/src/module/plugin-contribution-policy.ts +51 -0
- package/admin-core/src/module/tenant-modules.ts +20 -0
- package/admin-core/src/runtime/config.ts +55 -0
- package/admin-core/src/runtime/errors.ts +81 -0
- package/admin-core/src/runtime/guard.ts +40 -0
- package/admin-core/src/runtime/navigation.ts +105 -0
- package/admin-core/src/runtime/overrides.ts +214 -0
- package/admin-core/src/targets/store.ts +153 -0
- package/admin-shell/src/config.ts +84 -0
- package/admin-shell/src/deployment-mode.ts +36 -0
- package/admin-shell/src/index.ts +44 -0
- package/admin-shell/src/layout.ts +332 -0
- package/admin-shell/src/overrides.ts +53 -0
- package/admin-shell/src/states.ts +93 -0
- package/admin-shell/src/tabs.ts +31 -0
- package/admin-shell/src/targets.ts +128 -0
- package/admin-shell/src/theme.ts +15 -0
- package/client-core/src/index.ts +415 -0
- package/client-nuxt/src/index.ts +48 -0
- package/client-uniapp/src/index.ts +50 -0
- package/file-media/src/FileAssetSelector.vue +117 -0
- package/file-media/src/FileMediaPage.vue +158 -0
- package/file-media/src/contracts.ts +220 -0
- package/file-media/src/index.ts +19 -0
- package/file-media/src/runtime.ts +210 -0
- package/import-export/src/ImportExportPage.vue +155 -0
- package/import-export/src/contracts.ts +96 -0
- package/import-export/src/index.ts +3 -0
- package/import-export/src/runtime.ts +128 -0
- package/integration-security/src/IntegrationSecurityPage.vue +402 -0
- package/integration-security/src/contracts.ts +171 -0
- package/integration-security/src/index.ts +3 -0
- package/integration-security/src/runtime.ts +180 -0
- package/notification-sms/src/NotificationInboxPage.vue +266 -0
- package/notification-sms/src/contracts.ts +195 -0
- package/notification-sms/src/index.ts +4 -0
- package/notification-sms/src/runtime.ts +143 -0
- package/ops-console/src/OpsConsolePage.vue +337 -0
- package/ops-console/src/contracts.ts +169 -0
- package/ops-console/src/index.ts +3 -0
- package/ops-console/src/runtime.ts +199 -0
- package/package.json +139 -0
- package/reference-codes/src/ReferenceCodesPage.vue +942 -0
- package/reference-codes/src/contracts.ts +484 -0
- package/reference-codes/src/index.ts +53 -0
- package/reference-codes/src/runtime.ts +855 -0
- package/settings/src/SettingsPage.vue +536 -0
- package/settings/src/contracts.ts +331 -0
- package/settings/src/index.ts +45 -0
- package/settings/src/runtime.ts +545 -0
- package/task-job/src/TaskJobPage.vue +120 -0
- package/task-job/src/contracts.ts +117 -0
- package/task-job/src/index.ts +2 -0
- package/task-job/src/runtime.ts +105 -0
- package/testing/src/index.ts +141 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
export type SettingSourceScope = 'deployment' | 'tenant' | 'default' | null
|
|
2
|
+
export type SettingEditorKind = 'boolean' | 'enum' | 'number' | 'secret' | 'string' | 'unsupported'
|
|
3
|
+
export type SettingScalar = boolean | number | string
|
|
4
|
+
export type SettingSchemaType = 'array' | 'boolean' | 'integer' | 'null' | 'number' | 'object' | 'string'
|
|
5
|
+
|
|
6
|
+
export interface SettingSchema {
|
|
7
|
+
readonly type: SettingSchemaType | readonly SettingSchemaType[]
|
|
8
|
+
readonly enum?: readonly unknown[]
|
|
9
|
+
readonly [key: string]: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SettingRecord {
|
|
13
|
+
readonly moduleKey: string
|
|
14
|
+
readonly settingKey: string
|
|
15
|
+
readonly name: string
|
|
16
|
+
readonly description: string
|
|
17
|
+
readonly schema: SettingSchema
|
|
18
|
+
readonly required: boolean
|
|
19
|
+
readonly secret: boolean
|
|
20
|
+
readonly configured: boolean
|
|
21
|
+
readonly sourceScope: SettingSourceScope
|
|
22
|
+
readonly value: unknown
|
|
23
|
+
readonly effectiveAt: string | null
|
|
24
|
+
readonly expiresAt: string | null
|
|
25
|
+
readonly revision: string
|
|
26
|
+
readonly etag: string | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SettingGroup {
|
|
30
|
+
readonly moduleKey: string
|
|
31
|
+
readonly definitions: readonly SettingRecord[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SettingsTransportResult {
|
|
35
|
+
readonly body: unknown
|
|
36
|
+
readonly headers: Headers
|
|
37
|
+
readonly status: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type SettingPrecondition =
|
|
41
|
+
| { readonly kind: 'create' }
|
|
42
|
+
| { readonly kind: 'replace'; readonly etag: string }
|
|
43
|
+
|
|
44
|
+
export interface ReplaceSettingRequest {
|
|
45
|
+
readonly value: unknown
|
|
46
|
+
readonly idempotencyKey: string
|
|
47
|
+
readonly precondition: SettingPrecondition
|
|
48
|
+
readonly signal: AbortSignal
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface UnsetSettingRequest {
|
|
52
|
+
readonly idempotencyKey: string
|
|
53
|
+
readonly etag: string
|
|
54
|
+
readonly signal: AbortSignal
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SettingsTransport {
|
|
58
|
+
list: (signal: AbortSignal) => Promise<SettingsTransportResult>
|
|
59
|
+
replace: (
|
|
60
|
+
moduleKey: string,
|
|
61
|
+
settingKey: string,
|
|
62
|
+
request: ReplaceSettingRequest,
|
|
63
|
+
) => Promise<SettingsTransportResult>
|
|
64
|
+
unset: (
|
|
65
|
+
moduleKey: string,
|
|
66
|
+
settingKey: string,
|
|
67
|
+
request: UnsetSettingRequest,
|
|
68
|
+
) => Promise<SettingsTransportResult>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const moduleKeyPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*)*$/
|
|
72
|
+
const settingKeyPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|
73
|
+
const revisionPattern = /^[1-9][0-9]*$/
|
|
74
|
+
const strongEtagPattern = /^"[^"\r\n]+"$/
|
|
75
|
+
const schemaTypes = new Set<SettingSchemaType>(['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'])
|
|
76
|
+
const sourceScopes = new Set<Exclude<SettingSourceScope, null>>(['deployment', 'tenant', 'default'])
|
|
77
|
+
|
|
78
|
+
const invalidResponse = (): never => {
|
|
79
|
+
throw new Error('SETTINGS_RESPONSE_INVALID')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => (
|
|
83
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const stringField = (value: unknown, maximum: number): string => {
|
|
87
|
+
if (typeof value !== 'string' || value === '' || value.length > maximum) return invalidResponse()
|
|
88
|
+
return value
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const nullableDate = (value: unknown): string | null => {
|
|
92
|
+
if (value === null) return null
|
|
93
|
+
if (typeof value !== 'string' || !value.endsWith('Z') || Number.isNaN(Date.parse(value))) {
|
|
94
|
+
return invalidResponse()
|
|
95
|
+
}
|
|
96
|
+
return value
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const strongEtag = (value: unknown): string | null => {
|
|
100
|
+
if (value === null) return null
|
|
101
|
+
if (typeof value !== 'string' || !strongEtagPattern.test(value)) return invalidResponse()
|
|
102
|
+
return value
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const enumValues = (schema: Record<string, unknown>, type: SettingSchemaType): readonly unknown[] | undefined => {
|
|
106
|
+
if (!Object.hasOwn(schema, 'enum')) return undefined
|
|
107
|
+
if (!Array.isArray(schema.enum) || schema.enum.length === 0) return invalidResponse()
|
|
108
|
+
if (type === 'array' || type === 'object' || type === 'null') return [...schema.enum]
|
|
109
|
+
|
|
110
|
+
const values: SettingScalar[] = []
|
|
111
|
+
const seen = new Set<string>()
|
|
112
|
+
for (const value of schema.enum) {
|
|
113
|
+
const valid = type === 'string'
|
|
114
|
+
? typeof value === 'string'
|
|
115
|
+
: type === 'boolean'
|
|
116
|
+
? typeof value === 'boolean'
|
|
117
|
+
: type === 'number'
|
|
118
|
+
? typeof value === 'number' && Number.isFinite(value)
|
|
119
|
+
: type === 'integer'
|
|
120
|
+
? typeof value === 'number' && Number.isInteger(value)
|
|
121
|
+
: false
|
|
122
|
+
if (!valid) return invalidResponse()
|
|
123
|
+
const digest = `${typeof value}:${String(value)}`
|
|
124
|
+
if (seen.has(digest)) return invalidResponse()
|
|
125
|
+
seen.add(digest)
|
|
126
|
+
values.push(value as SettingScalar)
|
|
127
|
+
}
|
|
128
|
+
return values
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parseSchema = (value: unknown): SettingSchema => {
|
|
132
|
+
if (!isRecord(value)) return invalidResponse()
|
|
133
|
+
const type = typeof value.type === 'string'
|
|
134
|
+
? schemaTypes.has(value.type as SettingSchemaType) ? value.type as SettingSchemaType : invalidResponse()
|
|
135
|
+
: Array.isArray(value.type)
|
|
136
|
+
&& value.type.length > 0
|
|
137
|
+
&& new Set(value.type).size === value.type.length
|
|
138
|
+
&& value.type.every(item => typeof item === 'string' && schemaTypes.has(item as SettingSchemaType))
|
|
139
|
+
? value.type as SettingSchemaType[]
|
|
140
|
+
: invalidResponse()
|
|
141
|
+
const parsedEnum = Array.isArray(type)
|
|
142
|
+
? Object.hasOwn(value, 'enum')
|
|
143
|
+
? Array.isArray(value.enum) && value.enum.length > 0 ? [...value.enum] : invalidResponse()
|
|
144
|
+
: undefined
|
|
145
|
+
: enumValues(value, type)
|
|
146
|
+
|
|
147
|
+
return parsedEnum === undefined
|
|
148
|
+
? { ...value, type }
|
|
149
|
+
: { ...value, type, enum: parsedEnum }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const parseSourceScope = (value: unknown): SettingSourceScope => {
|
|
153
|
+
if (value === null) return null
|
|
154
|
+
if (typeof value !== 'string' || !sourceScopes.has(value as Exclude<SettingSourceScope, null>)) {
|
|
155
|
+
return invalidResponse()
|
|
156
|
+
}
|
|
157
|
+
return value as Exclude<SettingSourceScope, null>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const parseSettingValue = (value: unknown, schema: SettingSchema, allowsMissingValue: boolean): unknown => {
|
|
161
|
+
if (Array.isArray(schema.type) || schema.type === 'array' || schema.type === 'object' || schema.type === 'null') {
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
if (value === null) return allowsMissingValue ? null : invalidResponse()
|
|
165
|
+
|
|
166
|
+
const valid = schema.type === 'boolean'
|
|
167
|
+
? typeof value === 'boolean'
|
|
168
|
+
: schema.type === 'string'
|
|
169
|
+
? typeof value === 'string'
|
|
170
|
+
: schema.type === 'number'
|
|
171
|
+
? typeof value === 'number' && Number.isFinite(value)
|
|
172
|
+
: typeof value === 'number' && Number.isInteger(value)
|
|
173
|
+
if (!valid) return invalidResponse()
|
|
174
|
+
if (schema.enum !== undefined && !schema.enum.some(candidate => Object.is(candidate, value))) {
|
|
175
|
+
return invalidResponse()
|
|
176
|
+
}
|
|
177
|
+
return value
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export const parseSettingRecord = (value: unknown): SettingRecord => {
|
|
181
|
+
if (!isRecord(value)) return invalidResponse()
|
|
182
|
+
const moduleKey = stringField(value.module_key, 96)
|
|
183
|
+
const settingKey = stringField(value.setting_key, 64)
|
|
184
|
+
if (!moduleKeyPattern.test(moduleKey) || !settingKeyPattern.test(settingKey)) return invalidResponse()
|
|
185
|
+
if (
|
|
186
|
+
typeof value.required !== 'boolean'
|
|
187
|
+
|| typeof value.secret !== 'boolean'
|
|
188
|
+
|| typeof value.configured !== 'boolean'
|
|
189
|
+
|| typeof value.revision !== 'string'
|
|
190
|
+
|| !revisionPattern.test(value.revision)
|
|
191
|
+
) {
|
|
192
|
+
return invalidResponse()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const schema = parseSchema(value.schema)
|
|
196
|
+
const sourceScope = parseSourceScope(value.source_scope)
|
|
197
|
+
if (value.secret && (schema.type !== 'string' || value.value !== undefined)) {
|
|
198
|
+
throw new Error('SETTINGS_SECRET_RESPONSE_EXPOSED')
|
|
199
|
+
}
|
|
200
|
+
if (!value.secret && !Object.hasOwn(value, 'value')) return invalidResponse()
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
moduleKey,
|
|
204
|
+
settingKey,
|
|
205
|
+
name: stringField(value.name, 160),
|
|
206
|
+
description: stringField(value.description, 500),
|
|
207
|
+
schema,
|
|
208
|
+
required: value.required,
|
|
209
|
+
secret: value.secret,
|
|
210
|
+
configured: value.configured,
|
|
211
|
+
sourceScope,
|
|
212
|
+
value: value.secret
|
|
213
|
+
? null
|
|
214
|
+
: parseSettingValue(value.value, schema, !value.required && !value.configured && sourceScope === null),
|
|
215
|
+
effectiveAt: nullableDate(value.effective_at),
|
|
216
|
+
expiresAt: nullableDate(value.expires_at),
|
|
217
|
+
revision: value.revision,
|
|
218
|
+
etag: strongEtag(value.etag),
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const parseSettingsList = (value: unknown): SettingRecord[] => {
|
|
223
|
+
if (!isRecord(value) || !isRecord(value.data) || !Array.isArray(value.data.items)) {
|
|
224
|
+
return invalidResponse()
|
|
225
|
+
}
|
|
226
|
+
return value.data.items.map(parseSettingRecord)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const parseSettingResponse = (value: unknown, responseEtag?: string): SettingRecord => {
|
|
230
|
+
if (!isRecord(value) || !isRecord(value.data)) return invalidResponse()
|
|
231
|
+
return parseSettingRecord(responseEtag === undefined ? value.data : { ...value.data, etag: responseEtag })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const groupSettingRecords = (records: readonly SettingRecord[]): SettingGroup[] => {
|
|
235
|
+
const groups = new Map<string, SettingRecord[]>()
|
|
236
|
+
for (const record of records) {
|
|
237
|
+
const definitions = groups.get(record.moduleKey) ?? []
|
|
238
|
+
definitions.push(record)
|
|
239
|
+
groups.set(record.moduleKey, definitions)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return [...groups.entries()]
|
|
243
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
244
|
+
.map(([moduleKey, definitions]) => ({
|
|
245
|
+
moduleKey,
|
|
246
|
+
definitions: [...definitions].sort((left, right) => left.settingKey.localeCompare(right.settingKey)),
|
|
247
|
+
}))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const settingEditorKind = (record: SettingRecord): SettingEditorKind => {
|
|
251
|
+
if (record.secret) return 'secret'
|
|
252
|
+
if (Array.isArray(record.schema.type)) return 'unsupported'
|
|
253
|
+
if (record.schema.type !== 'array' && record.schema.type !== 'object' && record.schema.enum !== undefined) return 'enum'
|
|
254
|
+
if (record.schema.type === 'boolean') return 'boolean'
|
|
255
|
+
if (record.schema.type === 'integer' || record.schema.type === 'number') return 'number'
|
|
256
|
+
if (record.schema.type === 'string') return 'string'
|
|
257
|
+
return 'unsupported'
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface SettingsFetchTransportOptions {
|
|
261
|
+
readonly baseUrl: string
|
|
262
|
+
readonly fetch: (request: Request) => Promise<Response>
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const responseBody = async (response: Response): Promise<unknown> => {
|
|
266
|
+
const text = await response.text()
|
|
267
|
+
if (text === '') return null
|
|
268
|
+
try {
|
|
269
|
+
return JSON.parse(text) as unknown
|
|
270
|
+
} catch {
|
|
271
|
+
return null
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const transportResult = async (response: Response): Promise<SettingsTransportResult> => ({
|
|
276
|
+
body: await responseBody(response),
|
|
277
|
+
headers: response.headers,
|
|
278
|
+
status: response.status,
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const settingUrl = (baseUrl: string, moduleKey?: string, settingKey?: string): URL => {
|
|
282
|
+
const path = moduleKey === undefined || settingKey === undefined
|
|
283
|
+
? '/api/v1/settings'
|
|
284
|
+
: `/api/v1/settings/${encodeURIComponent(moduleKey)}/${encodeURIComponent(settingKey)}`
|
|
285
|
+
return new URL(path, baseUrl)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export const createSettingsFetchTransport = (options: SettingsFetchTransportOptions): SettingsTransport => ({
|
|
289
|
+
async list(signal) {
|
|
290
|
+
return transportResult(await options.fetch(new Request(settingUrl(options.baseUrl), {
|
|
291
|
+
credentials: 'include',
|
|
292
|
+
headers: { Accept: 'application/json' },
|
|
293
|
+
method: 'GET',
|
|
294
|
+
redirect: 'manual',
|
|
295
|
+
signal,
|
|
296
|
+
})))
|
|
297
|
+
},
|
|
298
|
+
async replace(moduleKey, settingKey, request) {
|
|
299
|
+
const headers = new Headers({
|
|
300
|
+
Accept: 'application/json',
|
|
301
|
+
'Content-Type': 'application/json',
|
|
302
|
+
'Idempotency-Key': request.idempotencyKey,
|
|
303
|
+
})
|
|
304
|
+
if (request.precondition.kind === 'create') {
|
|
305
|
+
headers.set('If-None-Match', '*')
|
|
306
|
+
} else {
|
|
307
|
+
headers.set('If-Match', request.precondition.etag)
|
|
308
|
+
}
|
|
309
|
+
return transportResult(await options.fetch(new Request(settingUrl(options.baseUrl, moduleKey, settingKey), {
|
|
310
|
+
body: JSON.stringify({ value: request.value }),
|
|
311
|
+
credentials: 'include',
|
|
312
|
+
headers,
|
|
313
|
+
method: 'PUT',
|
|
314
|
+
redirect: 'manual',
|
|
315
|
+
signal: request.signal,
|
|
316
|
+
})))
|
|
317
|
+
},
|
|
318
|
+
async unset(moduleKey, settingKey, request) {
|
|
319
|
+
return transportResult(await options.fetch(new Request(settingUrl(options.baseUrl, moduleKey, settingKey), {
|
|
320
|
+
credentials: 'include',
|
|
321
|
+
headers: {
|
|
322
|
+
Accept: 'application/json',
|
|
323
|
+
'Idempotency-Key': request.idempotencyKey,
|
|
324
|
+
'If-Match': request.etag,
|
|
325
|
+
},
|
|
326
|
+
method: 'DELETE',
|
|
327
|
+
redirect: 'manual',
|
|
328
|
+
signal: request.signal,
|
|
329
|
+
})))
|
|
330
|
+
},
|
|
331
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const SETTINGS_PACKAGE = '@peanut-admin/admin/settings' as const
|
|
2
|
+
export const SETTINGS_VERSION = '0.1.0' as const
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
createSettingsFetchTransport,
|
|
6
|
+
groupSettingRecords,
|
|
7
|
+
parseSettingRecord,
|
|
8
|
+
parseSettingResponse,
|
|
9
|
+
parseSettingsList,
|
|
10
|
+
settingEditorKind,
|
|
11
|
+
} from './contracts'
|
|
12
|
+
export type {
|
|
13
|
+
ReplaceSettingRequest,
|
|
14
|
+
SettingEditorKind,
|
|
15
|
+
SettingGroup,
|
|
16
|
+
SettingPrecondition,
|
|
17
|
+
SettingRecord,
|
|
18
|
+
SettingScalar,
|
|
19
|
+
SettingSchema,
|
|
20
|
+
SettingSourceScope,
|
|
21
|
+
SettingsFetchTransportOptions,
|
|
22
|
+
SettingsTransport,
|
|
23
|
+
SettingsTransportResult,
|
|
24
|
+
UnsetSettingRequest,
|
|
25
|
+
} from './contracts'
|
|
26
|
+
export {
|
|
27
|
+
createSettingsModuleContribution,
|
|
28
|
+
createSettingsRuntime,
|
|
29
|
+
SETTINGS_MANAGE_PERMISSION,
|
|
30
|
+
SETTINGS_MODULE_KEY,
|
|
31
|
+
SETTINGS_READ_PERMISSION,
|
|
32
|
+
SETTINGS_ROUTE_NAME,
|
|
33
|
+
SETTINGS_ROUTE_PATH,
|
|
34
|
+
SETTINGS_STORE_KEY,
|
|
35
|
+
settingsRuntimeKey,
|
|
36
|
+
useSettingsRuntime,
|
|
37
|
+
} from './runtime'
|
|
38
|
+
export type {
|
|
39
|
+
SettingConflictState,
|
|
40
|
+
SettingFormState,
|
|
41
|
+
SettingRequestError,
|
|
42
|
+
SettingsRuntime,
|
|
43
|
+
SettingsRuntimeOptions,
|
|
44
|
+
SettingsRuntimeState,
|
|
45
|
+
} from './runtime'
|