@peanut-admin/admin 0.1.0-alpha.2
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/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/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 +105 -0
- package/admin-core/src/lifecycle/tenant.ts +59 -0
- package/admin-core/src/module/contribution.ts +124 -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/index.ts +40 -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/targets.ts +128 -0
- package/admin-shell/src/theme.ts +15 -0
- package/client-core/src/index.ts +325 -0
- package/client-nuxt/src/index.ts +40 -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 +108 -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,484 @@
|
|
|
1
|
+
export type ReferenceCodeLifecycle = 'active' | 'retired'
|
|
2
|
+
export type ReferenceCodeStatus = 'active' | 'inactive'
|
|
3
|
+
export type ReferenceCodeMetadataScalar = boolean | number | string | null
|
|
4
|
+
export type ReferenceCodeMetadata = Readonly<Record<string, ReferenceCodeMetadataScalar>>
|
|
5
|
+
export type ReferenceCodeEffectiveStatusFilter = ReferenceCodeStatus | 'all'
|
|
6
|
+
|
|
7
|
+
export interface ReferenceCodeSetSummary {
|
|
8
|
+
readonly moduleKey: string
|
|
9
|
+
readonly setKey: string
|
|
10
|
+
readonly name: string
|
|
11
|
+
readonly description: string
|
|
12
|
+
readonly definitionRevision: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface EffectiveReferenceCodeVersion {
|
|
16
|
+
readonly revision: number
|
|
17
|
+
readonly label: string
|
|
18
|
+
readonly metadata: ReferenceCodeMetadata
|
|
19
|
+
readonly status: ReferenceCodeStatus
|
|
20
|
+
readonly sortOrder: number
|
|
21
|
+
readonly effectiveAt: string
|
|
22
|
+
readonly expiresAt: string | null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ReferenceCodeEntry {
|
|
26
|
+
readonly moduleKey: string
|
|
27
|
+
readonly setKey: string
|
|
28
|
+
readonly code: string
|
|
29
|
+
readonly lifecycle: ReferenceCodeLifecycle
|
|
30
|
+
readonly revision: number
|
|
31
|
+
readonly etag: string
|
|
32
|
+
readonly effective: EffectiveReferenceCodeVersion | null
|
|
33
|
+
readonly createdAt: string
|
|
34
|
+
readonly updatedAt: string
|
|
35
|
+
readonly retiredAt: string | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ReferenceCodeList {
|
|
39
|
+
readonly items: readonly ReferenceCodeEntry[]
|
|
40
|
+
readonly asOf: string
|
|
41
|
+
readonly page: number
|
|
42
|
+
readonly pageSize: number
|
|
43
|
+
readonly total: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ReferenceCodeListQuery {
|
|
47
|
+
readonly asOf: string
|
|
48
|
+
readonly effectiveStatus: ReferenceCodeEffectiveStatusFilter
|
|
49
|
+
readonly includeRetired: boolean
|
|
50
|
+
readonly page: number
|
|
51
|
+
readonly pageSize: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ReferenceCodeVersionInput {
|
|
55
|
+
readonly label: string
|
|
56
|
+
readonly metadata: ReferenceCodeMetadata
|
|
57
|
+
readonly status: ReferenceCodeStatus
|
|
58
|
+
readonly sortOrder: number
|
|
59
|
+
readonly effectiveAt: string
|
|
60
|
+
readonly expiresAt: string | null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ReferenceCodeCreateInput extends ReferenceCodeVersionInput {
|
|
64
|
+
readonly code: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ReferenceCodesTransportResult {
|
|
68
|
+
readonly body: unknown
|
|
69
|
+
readonly headers: Headers
|
|
70
|
+
readonly status: number
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ReferenceCodeCreateRequest {
|
|
74
|
+
readonly input: ReferenceCodeCreateInput
|
|
75
|
+
readonly idempotencyKey: string
|
|
76
|
+
readonly signal: AbortSignal
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ReferenceCodeReplaceRequest {
|
|
80
|
+
readonly input: ReferenceCodeVersionInput
|
|
81
|
+
readonly etag: string
|
|
82
|
+
readonly idempotencyKey: string
|
|
83
|
+
readonly signal: AbortSignal
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ReferenceCodeRetireRequest {
|
|
87
|
+
readonly etag: string
|
|
88
|
+
readonly idempotencyKey: string
|
|
89
|
+
readonly signal: AbortSignal
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ReferenceCodesTransport {
|
|
93
|
+
listSets: (signal: AbortSignal) => Promise<ReferenceCodesTransportResult>
|
|
94
|
+
listCodes: (
|
|
95
|
+
moduleKey: string,
|
|
96
|
+
setKey: string,
|
|
97
|
+
query: ReferenceCodeListQuery,
|
|
98
|
+
signal: AbortSignal,
|
|
99
|
+
) => Promise<ReferenceCodesTransportResult>
|
|
100
|
+
getCode: (
|
|
101
|
+
moduleKey: string,
|
|
102
|
+
setKey: string,
|
|
103
|
+
code: string,
|
|
104
|
+
asOf: string,
|
|
105
|
+
signal: AbortSignal,
|
|
106
|
+
) => Promise<ReferenceCodesTransportResult>
|
|
107
|
+
create: (
|
|
108
|
+
moduleKey: string,
|
|
109
|
+
setKey: string,
|
|
110
|
+
request: ReferenceCodeCreateRequest,
|
|
111
|
+
) => Promise<ReferenceCodesTransportResult>
|
|
112
|
+
replace: (
|
|
113
|
+
moduleKey: string,
|
|
114
|
+
setKey: string,
|
|
115
|
+
code: string,
|
|
116
|
+
request: ReferenceCodeReplaceRequest,
|
|
117
|
+
) => Promise<ReferenceCodesTransportResult>
|
|
118
|
+
retire: (
|
|
119
|
+
moduleKey: string,
|
|
120
|
+
setKey: string,
|
|
121
|
+
code: string,
|
|
122
|
+
request: ReferenceCodeRetireRequest,
|
|
123
|
+
) => Promise<ReferenceCodesTransportResult>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ReferenceCodesFetchTransportOptions {
|
|
127
|
+
readonly baseUrl: string
|
|
128
|
+
readonly fetch: (request: Request) => Promise<Response>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const moduleKeyPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*)*$/
|
|
132
|
+
const localKeyPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|
133
|
+
const etagPattern = /^"rev-[1-9][0-9]*"$/
|
|
134
|
+
const instantPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})(?:Z|([+-])(\d{2}):(\d{2}))$/
|
|
135
|
+
|
|
136
|
+
const invalidResponse = (): never => {
|
|
137
|
+
throw new Error('REFERENCE_CODES_RESPONSE_INVALID')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => (
|
|
141
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const hasExactKeys = (value: Record<string, unknown>, keys: readonly string[]): boolean => {
|
|
145
|
+
const actual = Object.keys(value).sort()
|
|
146
|
+
const expected = [...keys].sort()
|
|
147
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index])
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const exactRecord = (value: unknown, keys: readonly string[]): Record<string, unknown> => {
|
|
151
|
+
if (!isRecord(value) || !hasExactKeys(value, keys)) return invalidResponse()
|
|
152
|
+
return value
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const boundedString = (value: unknown, maximum: number, trim = false): string => {
|
|
156
|
+
if (typeof value !== 'string') return invalidResponse()
|
|
157
|
+
const normalized = trim ? value.trim() : value
|
|
158
|
+
if (normalized === '' || normalized !== value || [...normalized].length > maximum) return invalidResponse()
|
|
159
|
+
return normalized
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const positiveInteger = (value: unknown): number => {
|
|
163
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1) return invalidResponse()
|
|
164
|
+
return value
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const boundedInteger = (value: unknown, minimum: number, maximum: number): number => {
|
|
168
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
169
|
+
return invalidResponse()
|
|
170
|
+
}
|
|
171
|
+
return value
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const normalizeReferenceCodeInstant = (value: string): string => {
|
|
175
|
+
const match = instantPattern.exec(value)
|
|
176
|
+
if (match === null) throw new Error('REFERENCE_CODES_INSTANT_INVALID')
|
|
177
|
+
const year = Number(match[1])
|
|
178
|
+
const month = Number(match[2])
|
|
179
|
+
const day = Number(match[3])
|
|
180
|
+
const hour = Number(match[4])
|
|
181
|
+
const minute = Number(match[5])
|
|
182
|
+
const second = Number(match[6])
|
|
183
|
+
const offsetHour = match[9] === undefined ? 0 : Number(match[9])
|
|
184
|
+
const offsetMinute = match[10] === undefined ? 0 : Number(match[10])
|
|
185
|
+
const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
|
|
186
|
+
const daysInMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
187
|
+
if (
|
|
188
|
+
month < 1
|
|
189
|
+
|| month > 12
|
|
190
|
+
|| day < 1
|
|
191
|
+
|| day > (daysInMonth[month - 1] ?? 0)
|
|
192
|
+
|| hour > 23
|
|
193
|
+
|| minute > 59
|
|
194
|
+
|| second > 59
|
|
195
|
+
|| offsetHour > 23
|
|
196
|
+
|| offsetMinute > 59
|
|
197
|
+
) {
|
|
198
|
+
throw new Error('REFERENCE_CODES_INSTANT_INVALID')
|
|
199
|
+
}
|
|
200
|
+
const timestamp = Date.parse(value)
|
|
201
|
+
if (!Number.isFinite(timestamp)) throw new Error('REFERENCE_CODES_INSTANT_INVALID')
|
|
202
|
+
return new Date(timestamp).toISOString()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const instant = (value: unknown): string => {
|
|
206
|
+
if (typeof value !== 'string') return invalidResponse()
|
|
207
|
+
try {
|
|
208
|
+
return normalizeReferenceCodeInstant(value)
|
|
209
|
+
} catch {
|
|
210
|
+
return invalidResponse()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const nullableInstant = (value: unknown): string | null => value === null ? null : instant(value)
|
|
215
|
+
|
|
216
|
+
const moduleKey = (value: unknown): string => {
|
|
217
|
+
const parsed = boundedString(value, 96)
|
|
218
|
+
return moduleKeyPattern.test(parsed) ? parsed : invalidResponse()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const localKey = (value: unknown): string => {
|
|
222
|
+
const parsed = boundedString(value, 64)
|
|
223
|
+
return localKeyPattern.test(parsed) ? parsed : invalidResponse()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const strongEtag = (value: unknown): string => {
|
|
227
|
+
if (typeof value !== 'string' || !etagPattern.test(value)) return invalidResponse()
|
|
228
|
+
return value
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const metadata = (value: unknown): ReferenceCodeMetadata => {
|
|
232
|
+
if (!isRecord(value) || Object.keys(value).length > 32) return invalidResponse()
|
|
233
|
+
const parsed: Record<string, ReferenceCodeMetadataScalar> = {}
|
|
234
|
+
for (const [key, scalar] of Object.entries(value)) {
|
|
235
|
+
if (!localKeyPattern.test(key) || key.length > 64) return invalidResponse()
|
|
236
|
+
if (scalar === null || typeof scalar === 'boolean') {
|
|
237
|
+
parsed[key] = scalar
|
|
238
|
+
} else if (typeof scalar === 'number' && Number.isFinite(scalar)) {
|
|
239
|
+
parsed[key] = scalar
|
|
240
|
+
} else if (typeof scalar === 'string' && [...scalar].length <= 500) {
|
|
241
|
+
parsed[key] = scalar
|
|
242
|
+
} else {
|
|
243
|
+
return invalidResponse()
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (new TextEncoder().encode(JSON.stringify(parsed)).byteLength > 8192) return invalidResponse()
|
|
247
|
+
return parsed
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const parseReferenceCodeMetadataText = (value: string): ReferenceCodeMetadata => {
|
|
251
|
+
let parsed: unknown
|
|
252
|
+
try {
|
|
253
|
+
parsed = JSON.parse(value) as unknown
|
|
254
|
+
} catch {
|
|
255
|
+
throw new Error('REFERENCE_CODES_METADATA_INVALID')
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
return metadata(parsed)
|
|
259
|
+
} catch {
|
|
260
|
+
throw new Error('REFERENCE_CODES_METADATA_INVALID')
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const setSummary = (value: unknown): ReferenceCodeSetSummary => {
|
|
265
|
+
const record = exactRecord(value, [
|
|
266
|
+
'module_key', 'set_key', 'name', 'description', 'definition_revision',
|
|
267
|
+
])
|
|
268
|
+
return {
|
|
269
|
+
moduleKey: moduleKey(record.module_key),
|
|
270
|
+
setKey: localKey(record.set_key),
|
|
271
|
+
name: boundedString(record.name, 160, true),
|
|
272
|
+
description: boundedString(record.description, 500, true),
|
|
273
|
+
definitionRevision: positiveInteger(record.definition_revision),
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const effectiveVersion = (value: unknown): EffectiveReferenceCodeVersion | null => {
|
|
278
|
+
if (value === null) return null
|
|
279
|
+
const record = exactRecord(value, [
|
|
280
|
+
'revision', 'label', 'metadata', 'status', 'sort_order', 'effective_at', 'expires_at',
|
|
281
|
+
])
|
|
282
|
+
if (record.status !== 'active' && record.status !== 'inactive') return invalidResponse()
|
|
283
|
+
const effectiveAt = instant(record.effective_at)
|
|
284
|
+
const expiresAt = nullableInstant(record.expires_at)
|
|
285
|
+
if (expiresAt !== null && Date.parse(expiresAt) <= Date.parse(effectiveAt)) return invalidResponse()
|
|
286
|
+
return {
|
|
287
|
+
revision: positiveInteger(record.revision),
|
|
288
|
+
label: boundedString(record.label, 160, true),
|
|
289
|
+
metadata: metadata(record.metadata),
|
|
290
|
+
status: record.status,
|
|
291
|
+
sortOrder: boundedInteger(record.sort_order, -1_000_000, 1_000_000),
|
|
292
|
+
effectiveAt,
|
|
293
|
+
expiresAt,
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const entryRecord = (value: unknown, asOf?: string): ReferenceCodeEntry => {
|
|
298
|
+
const record = exactRecord(value, [
|
|
299
|
+
'module_key', 'set_key', 'code', 'lifecycle', 'revision', 'etag', 'effective',
|
|
300
|
+
'created_at', 'updated_at', 'retired_at',
|
|
301
|
+
])
|
|
302
|
+
if (record.lifecycle !== 'active' && record.lifecycle !== 'retired') return invalidResponse()
|
|
303
|
+
const retiredAt = nullableInstant(record.retired_at)
|
|
304
|
+
if (record.lifecycle === 'retired' && retiredAt === null) return invalidResponse()
|
|
305
|
+
if (asOf !== undefined) {
|
|
306
|
+
const retiredAtSnapshot = retiredAt !== null && Date.parse(retiredAt) <= Date.parse(asOf)
|
|
307
|
+
if ((record.lifecycle === 'retired') !== retiredAtSnapshot) return invalidResponse()
|
|
308
|
+
}
|
|
309
|
+
const revision = positiveInteger(record.revision)
|
|
310
|
+
const etag = strongEtag(record.etag)
|
|
311
|
+
if (etag !== `"rev-${revision}"`) return invalidResponse()
|
|
312
|
+
return {
|
|
313
|
+
moduleKey: moduleKey(record.module_key),
|
|
314
|
+
setKey: localKey(record.set_key),
|
|
315
|
+
code: localKey(record.code),
|
|
316
|
+
lifecycle: record.lifecycle,
|
|
317
|
+
revision,
|
|
318
|
+
etag,
|
|
319
|
+
effective: effectiveVersion(record.effective),
|
|
320
|
+
createdAt: instant(record.created_at),
|
|
321
|
+
updatedAt: instant(record.updated_at),
|
|
322
|
+
retiredAt,
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const dataEnvelope = (value: unknown): Record<string, unknown> => {
|
|
327
|
+
const envelope = exactRecord(value, ['data', 'meta'])
|
|
328
|
+
const meta = exactRecord(envelope.meta, ['request_id'])
|
|
329
|
+
if (typeof meta.request_id !== 'string' || meta.request_id === '') return invalidResponse()
|
|
330
|
+
if (!isRecord(envelope.data)) return invalidResponse()
|
|
331
|
+
return envelope.data
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export const parseReferenceCodeSets = (value: unknown): ReferenceCodeSetSummary[] => {
|
|
335
|
+
const data = exactRecord(dataEnvelope(value), ['items'])
|
|
336
|
+
if (!Array.isArray(data.items)) return invalidResponse()
|
|
337
|
+
return data.items.map(setSummary)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export const parseReferenceCode = (value: unknown, responseEtag?: string): ReferenceCodeEntry => {
|
|
341
|
+
const parsed = entryRecord(dataEnvelope(value))
|
|
342
|
+
if (responseEtag !== undefined) {
|
|
343
|
+
let parsedHeader: string
|
|
344
|
+
try {
|
|
345
|
+
parsedHeader = strongEtag(responseEtag)
|
|
346
|
+
} catch {
|
|
347
|
+
throw new Error('REFERENCE_CODES_RESPONSE_ETAG_INVALID')
|
|
348
|
+
}
|
|
349
|
+
if (parsedHeader !== parsed.etag) throw new Error('REFERENCE_CODES_RESPONSE_ETAG_MISMATCH')
|
|
350
|
+
}
|
|
351
|
+
return parsed
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export const parseReferenceCodeList = (value: unknown): ReferenceCodeList => {
|
|
355
|
+
const data = exactRecord(dataEnvelope(value), ['items', 'as_of', 'page', 'page_size', 'total'])
|
|
356
|
+
if (!Array.isArray(data.items)) return invalidResponse()
|
|
357
|
+
const asOf = instant(data.as_of)
|
|
358
|
+
const page = boundedInteger(data.page, 1, 10_000)
|
|
359
|
+
const pageSize = boundedInteger(data.page_size, 1, 100)
|
|
360
|
+
const total = boundedInteger(data.total, 0, Number.MAX_SAFE_INTEGER)
|
|
361
|
+
return {
|
|
362
|
+
items: data.items.map(item => entryRecord(item, asOf)),
|
|
363
|
+
asOf,
|
|
364
|
+
page,
|
|
365
|
+
pageSize,
|
|
366
|
+
total,
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const responseBody = async (response: Response): Promise<unknown> => {
|
|
371
|
+
const text = await response.text()
|
|
372
|
+
if (text === '') return null
|
|
373
|
+
try {
|
|
374
|
+
return JSON.parse(text) as unknown
|
|
375
|
+
} catch {
|
|
376
|
+
return null
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const transportResult = async (response: Response): Promise<ReferenceCodesTransportResult> => ({
|
|
381
|
+
body: await responseBody(response),
|
|
382
|
+
headers: response.headers,
|
|
383
|
+
status: response.status,
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
const resourceUrl = (
|
|
387
|
+
baseUrl: string,
|
|
388
|
+
moduleKeyValue?: string,
|
|
389
|
+
setKeyValue?: string,
|
|
390
|
+
codeValue?: string,
|
|
391
|
+
): URL => {
|
|
392
|
+
const segments = ['/api/v1/reference-code-sets']
|
|
393
|
+
if (moduleKeyValue !== undefined && setKeyValue !== undefined) {
|
|
394
|
+
segments.push(encodeURIComponent(moduleKeyValue), encodeURIComponent(setKeyValue), 'codes')
|
|
395
|
+
}
|
|
396
|
+
if (codeValue !== undefined) segments.push(encodeURIComponent(codeValue))
|
|
397
|
+
return new URL(segments.join('/'), baseUrl)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const versionBody = (input: ReferenceCodeVersionInput): Record<string, unknown> => ({
|
|
401
|
+
label: input.label,
|
|
402
|
+
metadata: input.metadata,
|
|
403
|
+
status: input.status,
|
|
404
|
+
sort_order: input.sortOrder,
|
|
405
|
+
effective_at: input.effectiveAt,
|
|
406
|
+
expires_at: input.expiresAt,
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
const jsonRequest = (url: URL, method: 'POST' | 'PUT', body: unknown, headers: HeadersInit, signal: AbortSignal): Request => (
|
|
410
|
+
new Request(url, {
|
|
411
|
+
method,
|
|
412
|
+
body: JSON.stringify(body),
|
|
413
|
+
credentials: 'include',
|
|
414
|
+
signal,
|
|
415
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json', ...headers },
|
|
416
|
+
})
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
export const createReferenceCodesFetchTransport = (
|
|
420
|
+
options: ReferenceCodesFetchTransportOptions,
|
|
421
|
+
): ReferenceCodesTransport => ({
|
|
422
|
+
async listSets(signal) {
|
|
423
|
+
return transportResult(await options.fetch(new Request(resourceUrl(options.baseUrl), {
|
|
424
|
+
credentials: 'include',
|
|
425
|
+
headers: { Accept: 'application/json' },
|
|
426
|
+
signal,
|
|
427
|
+
})))
|
|
428
|
+
},
|
|
429
|
+
async listCodes(moduleKeyValue, setKeyValue, query, signal) {
|
|
430
|
+
const url = resourceUrl(options.baseUrl, moduleKeyValue, setKeyValue)
|
|
431
|
+
url.searchParams.set('as_of', query.asOf)
|
|
432
|
+
url.searchParams.set('effective_status', query.effectiveStatus)
|
|
433
|
+
url.searchParams.set('include_retired', String(query.includeRetired))
|
|
434
|
+
url.searchParams.set('page', String(query.page))
|
|
435
|
+
url.searchParams.set('page_size', String(query.pageSize))
|
|
436
|
+
return transportResult(await options.fetch(new Request(url, {
|
|
437
|
+
credentials: 'include',
|
|
438
|
+
headers: { Accept: 'application/json' },
|
|
439
|
+
signal,
|
|
440
|
+
})))
|
|
441
|
+
},
|
|
442
|
+
async getCode(moduleKeyValue, setKeyValue, codeValue, asOf, signal) {
|
|
443
|
+
const url = resourceUrl(options.baseUrl, moduleKeyValue, setKeyValue, codeValue)
|
|
444
|
+
url.searchParams.set('as_of', asOf)
|
|
445
|
+
return transportResult(await options.fetch(new Request(url, {
|
|
446
|
+
credentials: 'include',
|
|
447
|
+
headers: { Accept: 'application/json' },
|
|
448
|
+
signal,
|
|
449
|
+
})))
|
|
450
|
+
},
|
|
451
|
+
async create(moduleKeyValue, setKeyValue, request) {
|
|
452
|
+
return transportResult(await options.fetch(jsonRequest(
|
|
453
|
+
resourceUrl(options.baseUrl, moduleKeyValue, setKeyValue),
|
|
454
|
+
'POST',
|
|
455
|
+
{ code: request.input.code, ...versionBody(request.input) },
|
|
456
|
+
{ 'Idempotency-Key': request.idempotencyKey, 'If-None-Match': '*' },
|
|
457
|
+
request.signal,
|
|
458
|
+
)))
|
|
459
|
+
},
|
|
460
|
+
async replace(moduleKeyValue, setKeyValue, codeValue, request) {
|
|
461
|
+
return transportResult(await options.fetch(jsonRequest(
|
|
462
|
+
resourceUrl(options.baseUrl, moduleKeyValue, setKeyValue, codeValue),
|
|
463
|
+
'PUT',
|
|
464
|
+
versionBody(request.input),
|
|
465
|
+
{ 'Idempotency-Key': request.idempotencyKey, 'If-Match': request.etag },
|
|
466
|
+
request.signal,
|
|
467
|
+
)))
|
|
468
|
+
},
|
|
469
|
+
async retire(moduleKeyValue, setKeyValue, codeValue, request) {
|
|
470
|
+
return transportResult(await options.fetch(new Request(
|
|
471
|
+
resourceUrl(options.baseUrl, moduleKeyValue, setKeyValue, codeValue),
|
|
472
|
+
{
|
|
473
|
+
method: 'DELETE',
|
|
474
|
+
credentials: 'include',
|
|
475
|
+
headers: {
|
|
476
|
+
Accept: 'application/json',
|
|
477
|
+
'Idempotency-Key': request.idempotencyKey,
|
|
478
|
+
'If-Match': request.etag,
|
|
479
|
+
},
|
|
480
|
+
signal: request.signal,
|
|
481
|
+
},
|
|
482
|
+
)))
|
|
483
|
+
},
|
|
484
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const REFERENCE_CODES_PACKAGE = '@peanut-admin/admin/reference-codes' as const
|
|
2
|
+
export const REFERENCE_CODES_VERSION = '0.1.0' as const
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
createReferenceCodesFetchTransport,
|
|
6
|
+
normalizeReferenceCodeInstant,
|
|
7
|
+
parseReferenceCode,
|
|
8
|
+
parseReferenceCodeList,
|
|
9
|
+
parseReferenceCodeMetadataText,
|
|
10
|
+
parseReferenceCodeSets,
|
|
11
|
+
} from './contracts'
|
|
12
|
+
export type {
|
|
13
|
+
EffectiveReferenceCodeVersion,
|
|
14
|
+
ReferenceCodeCreateInput,
|
|
15
|
+
ReferenceCodeCreateRequest,
|
|
16
|
+
ReferenceCodeEffectiveStatusFilter,
|
|
17
|
+
ReferenceCodeEntry,
|
|
18
|
+
ReferenceCodeLifecycle,
|
|
19
|
+
ReferenceCodeList,
|
|
20
|
+
ReferenceCodeListQuery,
|
|
21
|
+
ReferenceCodeMetadata,
|
|
22
|
+
ReferenceCodeMetadataScalar,
|
|
23
|
+
ReferenceCodeReplaceRequest,
|
|
24
|
+
ReferenceCodeRetireRequest,
|
|
25
|
+
ReferenceCodesFetchTransportOptions,
|
|
26
|
+
ReferenceCodeSetSummary,
|
|
27
|
+
ReferenceCodeStatus,
|
|
28
|
+
ReferenceCodesTransport,
|
|
29
|
+
ReferenceCodesTransportResult,
|
|
30
|
+
ReferenceCodeVersionInput,
|
|
31
|
+
} from './contracts'
|
|
32
|
+
export {
|
|
33
|
+
createReferenceCodesModuleContribution,
|
|
34
|
+
createReferenceCodesRuntime,
|
|
35
|
+
REFERENCE_CODES_MANAGE_PERMISSION,
|
|
36
|
+
REFERENCE_CODES_MODULE_KEY,
|
|
37
|
+
REFERENCE_CODES_READ_PERMISSION,
|
|
38
|
+
REFERENCE_CODES_ROUTE_NAME,
|
|
39
|
+
REFERENCE_CODES_ROUTE_PATH,
|
|
40
|
+
REFERENCE_CODES_STORE_KEY,
|
|
41
|
+
referenceCodesRuntimeKey,
|
|
42
|
+
useReferenceCodesRuntime,
|
|
43
|
+
} from './runtime'
|
|
44
|
+
export type {
|
|
45
|
+
ReferenceCodeAppendDraft,
|
|
46
|
+
ReferenceCodeCreateDraft,
|
|
47
|
+
ReferenceCodeDraftFields,
|
|
48
|
+
ReferenceCodeRequestError,
|
|
49
|
+
ReferenceCodesRuntime,
|
|
50
|
+
ReferenceCodesRuntimeOptions,
|
|
51
|
+
ReferenceCodesRuntimeState,
|
|
52
|
+
ReferenceCodeStaleState,
|
|
53
|
+
} from './runtime'
|