sovrium 0.0.2 → 0.2.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.
- package/README.md +67 -185
- package/package.json +26 -17
- package/src/application/use-cases/server/static-content-generators.ts +18 -2
- package/src/domain/models/api/activity.ts +87 -0
- package/src/domain/models/api/comments.ts +131 -0
- package/src/domain/models/api/index.ts +9 -0
- package/src/domain/models/api/params.ts +83 -0
- package/src/infrastructure/server/route-setup/openapi-routes/activity-routes.ts +72 -0
- package/src/infrastructure/server/route-setup/openapi-routes/analytics-routes.ts +176 -0
- package/src/infrastructure/server/route-setup/openapi-routes/batch-routes.ts +215 -0
- package/src/infrastructure/server/route-setup/openapi-routes/health-routes.ts +38 -0
- package/src/infrastructure/server/route-setup/openapi-routes/record-routes.ts +444 -0
- package/src/infrastructure/server/route-setup/openapi-routes/table-routes.ts +100 -0
- package/src/infrastructure/server/route-setup/openapi-routes/view-routes.ts +104 -0
- package/src/infrastructure/server/route-setup/openapi-schema.ts +40 -40
- package/src/presentation/styling/parse-style.ts +17 -9
- package/CHANGELOG.md +0 -3497
- package/schemas/0.0.1/app.openapi.json +0 -70
- package/schemas/0.0.1/app.schema.json +0 -7961
- package/schemas/0.0.2/app.openapi.json +0 -80
- package/schemas/0.0.2/app.schema.json +0 -8829
- package/schemas/development/app.openapi.json +0 -70
- package/schemas/development/app.schema.json +0 -7456
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
10
|
+
import { tableIdParamSchema } from '@/domain/models/api/params'
|
|
11
|
+
import {
|
|
12
|
+
getTablePermissionsResponseSchema,
|
|
13
|
+
getTableResponseSchema,
|
|
14
|
+
listTablesResponseSchema,
|
|
15
|
+
} from '@/domain/models/api/tables'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register table management routes for OpenAPI schema generation
|
|
19
|
+
*/
|
|
20
|
+
export function registerTableRoutes(app: OpenAPIHono): void {
|
|
21
|
+
// GET /api/tables
|
|
22
|
+
app.openapi(
|
|
23
|
+
createRoute({
|
|
24
|
+
method: 'get',
|
|
25
|
+
path: '/api/tables',
|
|
26
|
+
summary: 'List all tables',
|
|
27
|
+
description: 'Returns a list of all tables with summary information.',
|
|
28
|
+
operationId: 'listTables',
|
|
29
|
+
tags: ['tables'],
|
|
30
|
+
responses: {
|
|
31
|
+
200: {
|
|
32
|
+
content: { 'application/json': { schema: listTablesResponseSchema } },
|
|
33
|
+
description: 'List of tables',
|
|
34
|
+
},
|
|
35
|
+
401: {
|
|
36
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
37
|
+
description: 'Unauthorized',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
(c) => c.json({} as never)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// GET /api/tables/{tableId}
|
|
45
|
+
app.openapi(
|
|
46
|
+
createRoute({
|
|
47
|
+
method: 'get',
|
|
48
|
+
path: '/api/tables/{tableId}',
|
|
49
|
+
summary: 'Get table details',
|
|
50
|
+
description: 'Returns full table definition including fields, views, and permissions.',
|
|
51
|
+
operationId: 'getTable',
|
|
52
|
+
tags: ['tables'],
|
|
53
|
+
request: { params: tableIdParamSchema },
|
|
54
|
+
responses: {
|
|
55
|
+
200: {
|
|
56
|
+
content: { 'application/json': { schema: getTableResponseSchema } },
|
|
57
|
+
description: 'Table details',
|
|
58
|
+
},
|
|
59
|
+
401: {
|
|
60
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
61
|
+
description: 'Unauthorized',
|
|
62
|
+
},
|
|
63
|
+
404: {
|
|
64
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
65
|
+
description: 'Table not found',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
(c) => c.json({} as never)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
// GET /api/tables/{tableId}/permissions
|
|
73
|
+
app.openapi(
|
|
74
|
+
createRoute({
|
|
75
|
+
method: 'get',
|
|
76
|
+
path: '/api/tables/{tableId}/permissions',
|
|
77
|
+
summary: 'Get table permissions',
|
|
78
|
+
description:
|
|
79
|
+
"Returns the current user's permissions for the table, including field-level access.",
|
|
80
|
+
operationId: 'getTablePermissions',
|
|
81
|
+
tags: ['tables'],
|
|
82
|
+
request: { params: tableIdParamSchema },
|
|
83
|
+
responses: {
|
|
84
|
+
200: {
|
|
85
|
+
content: { 'application/json': { schema: getTablePermissionsResponseSchema } },
|
|
86
|
+
description: 'Table permissions',
|
|
87
|
+
},
|
|
88
|
+
401: {
|
|
89
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
90
|
+
description: 'Unauthorized',
|
|
91
|
+
},
|
|
92
|
+
404: {
|
|
93
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
94
|
+
description: 'Table not found',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
(c) => c.json({} as never)
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 ESSENTIAL SERVICES
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Business Source License 1.1
|
|
5
|
+
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type OpenAPIHono, createRoute } from '@hono/zod-openapi'
|
|
9
|
+
import { errorResponseSchema } from '@/domain/models/api/error'
|
|
10
|
+
import { tableIdParamSchema, viewIdParamSchema } from '@/domain/models/api/params'
|
|
11
|
+
import {
|
|
12
|
+
getViewRecordsResponseSchema,
|
|
13
|
+
getViewResponseSchema,
|
|
14
|
+
listViewsResponseSchema,
|
|
15
|
+
} from '@/domain/models/api/tables'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register view routes for OpenAPI schema generation
|
|
19
|
+
*/
|
|
20
|
+
export function registerViewRoutes(app: OpenAPIHono): void {
|
|
21
|
+
// GET /api/tables/{tableId}/views
|
|
22
|
+
app.openapi(
|
|
23
|
+
createRoute({
|
|
24
|
+
method: 'get',
|
|
25
|
+
path: '/api/tables/{tableId}/views',
|
|
26
|
+
summary: 'List views',
|
|
27
|
+
description: 'Returns all views defined for a table.',
|
|
28
|
+
operationId: 'listViews',
|
|
29
|
+
tags: ['views'],
|
|
30
|
+
request: { params: tableIdParamSchema },
|
|
31
|
+
responses: {
|
|
32
|
+
200: {
|
|
33
|
+
content: { 'application/json': { schema: listViewsResponseSchema } },
|
|
34
|
+
description: 'List of views',
|
|
35
|
+
},
|
|
36
|
+
401: {
|
|
37
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
38
|
+
description: 'Unauthorized',
|
|
39
|
+
},
|
|
40
|
+
404: {
|
|
41
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
42
|
+
description: 'Table not found',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
(c) => c.json({} as never)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// GET /api/tables/{tableId}/views/{viewId}
|
|
50
|
+
app.openapi(
|
|
51
|
+
createRoute({
|
|
52
|
+
method: 'get',
|
|
53
|
+
path: '/api/tables/{tableId}/views/{viewId}',
|
|
54
|
+
summary: 'Get view details',
|
|
55
|
+
description: 'Returns view configuration including filters, sorts, and visible fields.',
|
|
56
|
+
operationId: 'getView',
|
|
57
|
+
tags: ['views'],
|
|
58
|
+
request: { params: viewIdParamSchema },
|
|
59
|
+
responses: {
|
|
60
|
+
200: {
|
|
61
|
+
content: { 'application/json': { schema: getViewResponseSchema } },
|
|
62
|
+
description: 'View details',
|
|
63
|
+
},
|
|
64
|
+
401: {
|
|
65
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
66
|
+
description: 'Unauthorized',
|
|
67
|
+
},
|
|
68
|
+
404: {
|
|
69
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
70
|
+
description: 'View not found',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
(c) => c.json({} as never)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
// GET /api/tables/{tableId}/views/{viewId}/records
|
|
78
|
+
app.openapi(
|
|
79
|
+
createRoute({
|
|
80
|
+
method: 'get',
|
|
81
|
+
path: '/api/tables/{tableId}/views/{viewId}/records',
|
|
82
|
+
summary: 'Get records through a view',
|
|
83
|
+
description: 'Returns records filtered and sorted according to the view configuration.',
|
|
84
|
+
operationId: 'getViewRecords',
|
|
85
|
+
tags: ['views'],
|
|
86
|
+
request: { params: viewIdParamSchema },
|
|
87
|
+
responses: {
|
|
88
|
+
200: {
|
|
89
|
+
content: { 'application/json': { schema: getViewRecordsResponseSchema } },
|
|
90
|
+
description: 'Filtered records',
|
|
91
|
+
},
|
|
92
|
+
401: {
|
|
93
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
94
|
+
description: 'Unauthorized',
|
|
95
|
+
},
|
|
96
|
+
404: {
|
|
97
|
+
content: { 'application/json': { schema: errorResponseSchema } },
|
|
98
|
+
description: 'View not found',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
(c) => c.json({} as never)
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -5,8 +5,17 @@
|
|
|
5
5
|
* found in the LICENSE.md file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { OpenAPIHono
|
|
9
|
-
import {
|
|
8
|
+
import { OpenAPIHono } from '@hono/zod-openapi'
|
|
9
|
+
import { registerActivityRoutes } from './openapi-routes/activity-routes'
|
|
10
|
+
import { registerAnalyticsRoutes } from './openapi-routes/analytics-routes'
|
|
11
|
+
import { registerBatchRoutes } from './openapi-routes/batch-routes'
|
|
12
|
+
import { registerHealthRoutes } from './openapi-routes/health-routes'
|
|
13
|
+
import { registerRecordRoutes } from './openapi-routes/record-routes'
|
|
14
|
+
import { registerTableRoutes } from './openapi-routes/table-routes'
|
|
15
|
+
import { registerViewRoutes } from './openapi-routes/view-routes'
|
|
16
|
+
|
|
17
|
+
// Read version from package.json at module load (avoids JSON import lint issues)
|
|
18
|
+
const { version: APP_VERSION } = await Bun.file('package.json').json()
|
|
10
19
|
|
|
11
20
|
/**
|
|
12
21
|
* OpenAPI Schema Generator
|
|
@@ -25,7 +34,7 @@ import { healthResponseSchema } from '@/domain/models/api/health'
|
|
|
25
34
|
* **Usage**:
|
|
26
35
|
* 1. Runtime: Server uses regular Hono routes from api-routes.ts
|
|
27
36
|
* 2. Docs: Server exposes this schema at `/api/openapi.json` and `/api/scalar`
|
|
28
|
-
* 3. Export: Script exports this schema to `schemas/
|
|
37
|
+
* 3. Export: Script exports this schema to `schemas/{version}/app.openapi.json`
|
|
29
38
|
*/
|
|
30
39
|
|
|
31
40
|
/**
|
|
@@ -38,42 +47,13 @@ import { healthResponseSchema } from '@/domain/models/api/health'
|
|
|
38
47
|
const createOpenApiApp = () => {
|
|
39
48
|
const app = new OpenAPIHono()
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
operationId: 'healthCheck',
|
|
49
|
-
tags: ['infrastructure'],
|
|
50
|
-
responses: {
|
|
51
|
-
200: {
|
|
52
|
-
content: {
|
|
53
|
-
'application/json': {
|
|
54
|
-
schema: healthResponseSchema,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
description: 'Server is healthy',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Mount route with dummy handler (only for schema generation)
|
|
63
|
-
|
|
64
|
-
app.openapi(healthRoute, (c) => {
|
|
65
|
-
return c.json({
|
|
66
|
-
status: 'ok',
|
|
67
|
-
timestamp: new Date().toISOString(),
|
|
68
|
-
app: {
|
|
69
|
-
name: 'Sovrium',
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
// Future routes will be added here:
|
|
75
|
-
// app.openapi(listTablesRoute, ...)
|
|
76
|
-
// app.openapi(getTableRoute, ...)
|
|
50
|
+
registerHealthRoutes(app)
|
|
51
|
+
registerTableRoutes(app)
|
|
52
|
+
registerRecordRoutes(app)
|
|
53
|
+
registerBatchRoutes(app)
|
|
54
|
+
registerViewRoutes(app)
|
|
55
|
+
registerActivityRoutes(app)
|
|
56
|
+
registerAnalyticsRoutes(app)
|
|
77
57
|
|
|
78
58
|
return app
|
|
79
59
|
}
|
|
@@ -96,7 +76,7 @@ export const getOpenAPIDocument = () => {
|
|
|
96
76
|
openapi: '3.1.0',
|
|
97
77
|
info: {
|
|
98
78
|
title: 'Sovrium API',
|
|
99
|
-
version:
|
|
79
|
+
version: APP_VERSION as string,
|
|
100
80
|
description:
|
|
101
81
|
'REST API specification for Sovrium application.\n\n' +
|
|
102
82
|
'**Generated Schema**: This schema is automatically generated from the runtime implementation. ' +
|
|
@@ -115,6 +95,26 @@ export const getOpenAPIDocument = () => {
|
|
|
115
95
|
name: 'infrastructure',
|
|
116
96
|
description: 'Infrastructure endpoints (health, metrics)',
|
|
117
97
|
},
|
|
98
|
+
{
|
|
99
|
+
name: 'tables',
|
|
100
|
+
description: 'Table management endpoints',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'records',
|
|
104
|
+
description: 'Record CRUD, comments, and history endpoints',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'views',
|
|
108
|
+
description: 'View management and filtered record access',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'activity',
|
|
112
|
+
description: 'Activity log and audit trail endpoints',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'analytics',
|
|
116
|
+
description: 'Analytics collection and reporting endpoints',
|
|
117
|
+
},
|
|
118
118
|
],
|
|
119
119
|
})
|
|
120
120
|
}
|
|
@@ -196,18 +196,26 @@ export function parseStyle(styleString: string): Record<string, string> {
|
|
|
196
196
|
|
|
197
197
|
// Use reduce for immutable accumulation instead of for-of loop with mutations
|
|
198
198
|
return declarations.reduce<Record<string, string>>((acc, declaration) => {
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
// Convert kebab-case to camelCase (e.g., background-color → backgroundColor)
|
|
202
|
-
const camelCaseProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
199
|
+
const colonIndex = declaration.indexOf(':')
|
|
200
|
+
if (colonIndex === -1) return acc
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
202
|
+
const property = declaration.slice(0, colonIndex).trim()
|
|
203
|
+
const value = declaration.slice(colonIndex + 1).trim()
|
|
204
|
+
if (!property || !value) return acc
|
|
207
205
|
|
|
208
|
-
|
|
206
|
+
// CSS custom properties (--*) must be preserved as-is, not converted to camelCase
|
|
207
|
+
if (property.startsWith('--')) {
|
|
208
|
+
return { ...acc, [property]: value }
|
|
209
209
|
}
|
|
210
|
-
|
|
210
|
+
|
|
211
|
+
// Convert kebab-case to camelCase (e.g., background-color → backgroundColor)
|
|
212
|
+
const camelCaseProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
213
|
+
|
|
214
|
+
// Normalize animation names to kebab-case
|
|
215
|
+
const normalizedValue =
|
|
216
|
+
camelCaseProperty === 'animation' ? normalizeAnimationValue(value) : value
|
|
217
|
+
|
|
218
|
+
return { ...acc, [camelCaseProperty]: normalizedValue }
|
|
211
219
|
}, {})
|
|
212
220
|
}
|
|
213
221
|
|