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.
@@ -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, createRoute } from '@hono/zod-openapi'
9
- import { healthResponseSchema } from '@/domain/models/api/health'
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/0.0.1/app.openapi.json`
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
- // Define health check route with OpenAPI annotations
42
- const healthRoute = createRoute({
43
- method: 'get',
44
- path: '/api/health',
45
- summary: 'Health check endpoint',
46
- description:
47
- 'Returns server health status. Used by monitoring tools and E2E tests to verify server is running.',
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: '0.0.1',
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 [property, value] = declaration.split(':').map((s) => s.trim())
200
- if (property && value) {
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
- // Normalize animation names to kebab-case
205
- const normalizedValue =
206
- camelCaseProperty === 'animation' ? normalizeAnimationValue(value) : value
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
- return { ...acc, [camelCaseProperty]: normalizedValue }
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
- return acc
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