codehooks-js 1.3.24 → 1.4.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,92 @@
1
+ /**
2
+ * Swagger UI HTML generator
3
+ * Generates HTML that loads Swagger UI from CDN
4
+ */
5
+
6
+ const SWAGGER_UI_VERSION = '5.11.0';
7
+
8
+ /**
9
+ * Generate Swagger UI HTML page
10
+ * @param {string} specUrl - URL to the OpenAPI JSON spec
11
+ * @param {object} config - Configuration options
12
+ * @param {string} [config.title] - Page title
13
+ * @param {string} [config.oauth2RedirectUrl] - OAuth2 redirect URL
14
+ * @param {string|null} [config.validatorUrl] - Validator URL (null to disable)
15
+ * @param {string} [config.favicon] - Favicon URL
16
+ * @returns {string} HTML string
17
+ */
18
+ export function generateSwaggerHtml(specUrl, config = {}) {
19
+ const title = config.title || 'API Documentation';
20
+ const favicon = config.favicon || 'https://unpkg.com/swagger-ui-dist@' + SWAGGER_UI_VERSION + '/favicon-32x32.png';
21
+
22
+ return `<!DOCTYPE html>
23
+ <html lang="en">
24
+ <head>
25
+ <meta charset="UTF-8">
26
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
+ <title>${escapeHtml(title)}</title>
28
+ <link rel="icon" type="image/png" href="${escapeHtml(favicon)}">
29
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@${SWAGGER_UI_VERSION}/swagger-ui.css">
30
+ <style>
31
+ html { box-sizing: border-box; overflow-y: scroll; }
32
+ *, *:before, *:after { box-sizing: inherit; }
33
+ body { margin: 0; padding: 0; background: #fafafa; }
34
+ .swagger-ui .topbar { display: none; }
35
+ .swagger-ui .info { margin: 30px 0; }
36
+ .swagger-ui .info .title { font-size: 2em; }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ <div id="swagger-ui"></div>
41
+ <script src="https://unpkg.com/swagger-ui-dist@${SWAGGER_UI_VERSION}/swagger-ui-bundle.js" crossorigin></script>
42
+ <script src="https://unpkg.com/swagger-ui-dist@${SWAGGER_UI_VERSION}/swagger-ui-standalone-preset.js" crossorigin></script>
43
+ <script>
44
+ window.onload = function() {
45
+ const ui = SwaggerUIBundle({
46
+ url: "${escapeJs(specUrl)}",
47
+ dom_id: '#swagger-ui',
48
+ deepLinking: true,
49
+ presets: [
50
+ SwaggerUIBundle.presets.apis,
51
+ SwaggerUIStandalonePreset
52
+ ],
53
+ plugins: [
54
+ SwaggerUIBundle.plugins.DownloadUrl
55
+ ],
56
+ layout: "StandaloneLayout"${config.oauth2RedirectUrl ? `,
57
+ oauth2RedirectUrl: "${escapeJs(config.oauth2RedirectUrl)}"` : ''}${config.validatorUrl !== undefined ? `,
58
+ validatorUrl: ${config.validatorUrl === null ? 'null' : `"${escapeJs(config.validatorUrl)}"`}` : ''}
59
+ });
60
+ window.ui = ui;
61
+ };
62
+ </script>
63
+ </body>
64
+ </html>`;
65
+ }
66
+
67
+ /**
68
+ * Escape HTML special characters
69
+ * @param {string} str
70
+ * @returns {string}
71
+ */
72
+ function escapeHtml(str) {
73
+ return String(str)
74
+ .replace(/&/g, '&amp;')
75
+ .replace(/</g, '&lt;')
76
+ .replace(/>/g, '&gt;')
77
+ .replace(/"/g, '&quot;')
78
+ .replace(/'/g, '&#039;');
79
+ }
80
+
81
+ /**
82
+ * Escape string for JavaScript
83
+ * @param {string} str
84
+ * @returns {string}
85
+ */
86
+ function escapeJs(str) {
87
+ return String(str)
88
+ .replace(/\\/g, '\\\\')
89
+ .replace(/"/g, '\\"')
90
+ .replace(/\n/g, '\\n')
91
+ .replace(/\r/g, '\\r');
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.24",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "description": "Codehooks.io official library - provides express.JS like syntax",
6
6
  "main": "index.js",
@@ -18,6 +18,11 @@
18
18
  "./crudlify/lib/schema/zod/index.mjs",
19
19
  "./workflow/index.mjs",
20
20
  "./workflow/engine.mjs",
21
+ "./openapi/index.mjs",
22
+ "./openapi/generator.mjs",
23
+ "./openapi/schema-converter.mjs",
24
+ "./openapi/swagger-ui.mjs",
25
+ "./openapi/crudlify-docs.mjs",
21
26
  "./types",
22
27
  "tsconfig.json"
23
28
  ],
@@ -44,7 +49,9 @@
44
49
  "queue",
45
50
  "cron",
46
51
  "schedule",
47
- "mongodb"
52
+ "mongodb",
53
+ "openapi",
54
+ "swagger"
48
55
  ],
49
56
  "dependencies": {
50
57
  "lodash": "^4.17.21",
@@ -53,6 +60,8 @@
53
60
  },
54
61
  "devDependencies": {
55
62
  "@types/mime": "^3.0.4",
56
- "@types/node": "^22.15.19"
63
+ "@types/node": "^22.15.19",
64
+ "yup": "^1.7.1",
65
+ "zod": "^4.3.6"
57
66
  }
58
67
  }
package/types/index.d.ts CHANGED
@@ -313,8 +313,9 @@ export type DatastoreAPI = {
313
313
  collection: (collection: string) => NoSQLAPI;
314
314
  /**
315
315
  * - Get object by ID (objectID) or query (NoSQL query {...}), returns a Promise with the object
316
+ * @throws {Error} Rejects if the object is not found
316
317
  */
317
- getOne: (collection: string, query: string | object) => Promise<any>;
318
+ getOne: (collection: string, query: string | object) => Promise<object>;
318
319
  /**
319
320
  * - Get stream of objects by query
320
321
  * * https://codehooks.io/docs/nosql-database-api#getmanycollection-query-options
@@ -322,12 +323,13 @@ export type DatastoreAPI = {
322
323
  getMany: (collection: string, query?: object, options?: object) => DataStream;
323
324
  /**
324
325
  * - Get object by ID (objectID) or query (NoSQL query {...}), returns a Promise with the object (alias for getOne)
326
+ * @throws {Error} Rejects if the object is not found
325
327
  */
326
- findOne: (collection: string, query: string | object) => Promise<any>;
328
+ findOne: (collection: string, query: string | object) => Promise<object>;
327
329
  /**
328
330
  * - Get object by ID (objectID) or query (NoSQL query {...}), returns a Promise with the object or null if not found
329
331
  */
330
- findOneOrNull: (collection: string, query: string | object) => Promise<any>;
332
+ findOneOrNull: (collection: string, query: string | object) => Promise<object | null>;
331
333
  /**
332
334
  * - Alias for getMany
333
335
  * https://codehooks.io/docs/nosql-database-api#getmanycollection-query-options
@@ -344,14 +346,14 @@ export type DatastoreAPI = {
344
346
  * @param query string | object
345
347
  * @param updateoperators object
346
348
  * @param options object - {upsert: true}
347
- * @returns Promise<any>
349
+ * @returns Promise<object>
348
350
  */
349
351
  updateOne: (
350
352
  collection: string,
351
353
  query: string | object,
352
354
  updateoperators?: object,
353
355
  options?: object
354
- ) => Promise<any>;
356
+ ) => Promise<object>;
355
357
  /**
356
358
  * - Update multiple data objects in a datastore collection.
357
359
  * - Input document is patched with the matched database objects
@@ -361,7 +363,7 @@ export type DatastoreAPI = {
361
363
  query: object,
362
364
  document: object,
363
365
  updateoperators?: object
364
- ) => Promise<any>;
366
+ ) => Promise<object>;
365
367
  /**
366
368
  * - Replace one data object by ID in a datastore collection.
367
369
  * - Input document overwrites the matched database object, the _id value is not overwritten.
@@ -369,8 +371,9 @@ export type DatastoreAPI = {
369
371
  replaceOne: (
370
372
  collection: string,
371
373
  query: string | object,
374
+ document: object,
372
375
  options?: object
373
- ) => Promise<any>;
376
+ ) => Promise<object>;
374
377
  /**
375
378
  * - Replace multiple data objects in a datastore collection.
376
379
  * - Input document overwrites the matched database objects. The _id value is not overwritten
@@ -378,16 +381,17 @@ export type DatastoreAPI = {
378
381
  replaceMany: (
379
382
  collection: string,
380
383
  query: object,
384
+ document: object,
381
385
  options?: object
382
- ) => Promise<any>;
386
+ ) => Promise<object>;
383
387
  /**
384
388
  * - Remove one data object by ID in a collection in the current Datastore
385
389
  */
386
- removeOne: (collection: string, query: string | object) => Promise<any>;
390
+ removeOne: (collection: string, query: string | object) => Promise<object>;
387
391
  /**
388
392
  * - Remove multiple data objects in a collection in the current Datastore
389
393
  */
390
- removeMany: (collection: string, query: object) => Promise<any>;
394
+ removeMany: (collection: string, query: object) => Promise<object>;
391
395
  /**
392
396
  * - Set a key-value pair in the current Datastore
393
397
  */
@@ -1081,6 +1085,22 @@ export class Codehooks {
1081
1085
  * @returns Promise with the fetched data
1082
1086
  */
1083
1087
  internalFetch: (url: string, options?: any) => Promise<any>;
1088
+ /**
1089
+ * Enable OpenAPI documentation
1090
+ * @param config - OpenAPI configuration
1091
+ * @param uiPath - Path to serve Swagger UI (default: '/docs')
1092
+ * @returns this for chaining
1093
+ * @example
1094
+ * app.openapi({
1095
+ * info: {
1096
+ * title: "My API",
1097
+ * version: "1.0.0"
1098
+ * }
1099
+ * }, '/docs');
1100
+ */
1101
+ openapi: (config?: OpenAPIConfig, uiPath?: string) => Codehooks;
1102
+ /** Stored OpenAPI metadata for routes */
1103
+ openApiMeta: Record<string, OpenAPIRouteSpec>;
1084
1104
  }
1085
1105
  declare const _coho: Codehooks;
1086
1106
 
@@ -1500,4 +1520,244 @@ export type WorkflowEventData = {
1500
1520
  [key: string]: any;
1501
1521
  };
1502
1522
 
1523
+ // ============================================
1524
+ // OpenAPI Documentation Types
1525
+ // ============================================
1526
+
1527
+ /**
1528
+ * OpenAPI JSON Schema type (subset)
1529
+ */
1530
+ export type OpenAPISchema = {
1531
+ type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
1532
+ format?: string;
1533
+ items?: OpenAPISchema;
1534
+ properties?: Record<string, OpenAPISchema>;
1535
+ required?: string[];
1536
+ $ref?: string;
1537
+ enum?: any[];
1538
+ default?: any;
1539
+ example?: any;
1540
+ description?: string;
1541
+ nullable?: boolean;
1542
+ allOf?: OpenAPISchema[];
1543
+ oneOf?: OpenAPISchema[];
1544
+ anyOf?: OpenAPISchema[];
1545
+ [key: string]: any;
1546
+ };
1547
+
1548
+ /**
1549
+ * OpenAPI Parameter specification
1550
+ */
1551
+ export type OpenAPIParameter = {
1552
+ name: string;
1553
+ in: 'path' | 'query' | 'header' | 'cookie';
1554
+ description?: string;
1555
+ required?: boolean;
1556
+ schema: OpenAPISchema;
1557
+ example?: any;
1558
+ };
1559
+
1560
+ /**
1561
+ * OpenAPI Request Body specification
1562
+ */
1563
+ export type OpenAPIRequestBody = {
1564
+ description?: string;
1565
+ required?: boolean;
1566
+ content: Record<string, { schema: OpenAPISchema; example?: any }>;
1567
+ };
1568
+
1569
+ /**
1570
+ * OpenAPI Response specification
1571
+ */
1572
+ export type OpenAPIResponse = {
1573
+ description: string;
1574
+ content?: Record<string, { schema: OpenAPISchema; example?: any }>;
1575
+ headers?: Record<string, { description?: string; schema: OpenAPISchema }>;
1576
+ };
1577
+
1578
+ /**
1579
+ * OpenAPI Route specification for openapi() wrapper
1580
+ */
1581
+ export type OpenAPIRouteSpec = {
1582
+ /** Short summary of what the operation does */
1583
+ summary?: string;
1584
+ /** Verbose explanation of the operation */
1585
+ description?: string;
1586
+ /** Tags for API documentation grouping */
1587
+ tags?: string[];
1588
+ /** Unique operation identifier for code generation */
1589
+ operationId?: string;
1590
+ /** Parameters (path, query, header, cookie) */
1591
+ parameters?: OpenAPIParameter[];
1592
+ /** Request body specification */
1593
+ requestBody?: OpenAPIRequestBody;
1594
+ /** Response specifications by status code */
1595
+ responses?: Record<string, OpenAPIResponse>;
1596
+ /** Security requirements for this operation */
1597
+ security?: Array<Record<string, string[]>>;
1598
+ /** Whether this operation is deprecated */
1599
+ deprecated?: boolean;
1600
+ };
1601
+
1602
+ /**
1603
+ * OpenAPI Info object
1604
+ */
1605
+ export type OpenAPIInfo = {
1606
+ title?: string;
1607
+ description?: string;
1608
+ version?: string;
1609
+ termsOfService?: string;
1610
+ contact?: { name?: string; url?: string; email?: string };
1611
+ license?: { name: string; url?: string };
1612
+ };
1613
+
1614
+ /**
1615
+ * OpenAPI Server object
1616
+ */
1617
+ export type OpenAPIServer = {
1618
+ url: string;
1619
+ description?: string;
1620
+ variables?: Record<string, { default: string; enum?: string[]; description?: string }>;
1621
+ };
1622
+
1623
+ /**
1624
+ * OpenAPI Tag object
1625
+ */
1626
+ export type OpenAPITag = {
1627
+ name: string;
1628
+ description?: string;
1629
+ externalDocs?: { url: string; description?: string };
1630
+ };
1631
+
1632
+ /**
1633
+ * Filter operation object passed to the filter function
1634
+ */
1635
+ export type OpenAPIFilterOperation = {
1636
+ /** HTTP method (lowercase): 'get', 'post', 'put', 'patch', 'delete' */
1637
+ method: string;
1638
+ /** Route path: '/todos', '/todos/{ID}' */
1639
+ path: string;
1640
+ /** Operation ID if set */
1641
+ operationId?: string;
1642
+ /** Array of tags */
1643
+ tags: string[];
1644
+ /** Operation summary */
1645
+ summary?: string;
1646
+ };
1647
+
1648
+ /**
1649
+ * OpenAPI Configuration for app.openapi()
1650
+ */
1651
+ export type OpenAPIConfig = {
1652
+ /** OpenAPI Info object */
1653
+ info?: OpenAPIInfo;
1654
+ /** Server definitions */
1655
+ servers?: OpenAPIServer[];
1656
+ /** Custom schemas to add to components */
1657
+ components?: {
1658
+ schemas?: Record<string, OpenAPISchema>;
1659
+ securitySchemes?: Record<string, any>;
1660
+ };
1661
+ /** Global security requirements */
1662
+ security?: Array<Record<string, string[]>>;
1663
+ /** Path to serve the OpenAPI JSON spec (default: '/openapi.json') */
1664
+ specPath?: string;
1665
+ /** Swagger UI configuration */
1666
+ swaggerUi?: {
1667
+ oauth2RedirectUrl?: string;
1668
+ validatorUrl?: string | null;
1669
+ favicon?: string;
1670
+ };
1671
+ /** Tags for grouping endpoints */
1672
+ tags?: OpenAPITag[];
1673
+ /** External documentation */
1674
+ externalDocs?: { url: string; description?: string };
1675
+ /**
1676
+ * Filter function to control which operations appear in the documentation.
1677
+ * Return true to include the operation, false to exclude it.
1678
+ * @example
1679
+ * // Exclude DELETE operations
1680
+ * filter: (op) => op.method !== 'delete'
1681
+ *
1682
+ * // Exclude internal routes
1683
+ * filter: (op) => !op.path.startsWith('/internal')
1684
+ *
1685
+ * // Only include specific tags
1686
+ * filter: (op) => op.tags.includes('Public')
1687
+ */
1688
+ filter?: (op: OpenAPIFilterOperation) => boolean;
1689
+ };
1690
+
1691
+ /**
1692
+ * Wrap a route handler with OpenAPI documentation
1693
+ * @param spec - OpenAPI specification for this route
1694
+ * @returns Middleware function with attached metadata
1695
+ * @example
1696
+ * app.get('/users/:id',
1697
+ * openapi({
1698
+ * summary: "Get user by ID",
1699
+ * tags: ["Users"],
1700
+ * responses: { 200: { description: "Success" } }
1701
+ * }),
1702
+ * (req, res) => res.json({ id: req.params.id })
1703
+ * );
1704
+ */
1705
+ export function openapi(spec: OpenAPIRouteSpec): (
1706
+ req: httpRequest,
1707
+ res: httpResponse,
1708
+ next: nextFunction
1709
+ ) => void;
1710
+
1711
+ /**
1712
+ * Helper to create response specification
1713
+ * @param description - Response description
1714
+ * @param schema - Response schema (JSON Schema or Zod/Yup)
1715
+ * @param contentType - Content type (default: 'application/json')
1716
+ */
1717
+ export function response(
1718
+ description: string,
1719
+ schema?: OpenAPISchema,
1720
+ contentType?: string
1721
+ ): OpenAPIResponse;
1722
+
1723
+ /**
1724
+ * Helper to create request body specification
1725
+ * @param schema - Request body schema
1726
+ * @param options - Additional options
1727
+ */
1728
+ export function body(
1729
+ schema: OpenAPISchema,
1730
+ options?: { required?: boolean; description?: string; contentType?: string }
1731
+ ): OpenAPIRequestBody;
1732
+
1733
+ /**
1734
+ * Helper to create path parameter specification
1735
+ * @param name - Parameter name
1736
+ * @param options - Parameter options
1737
+ */
1738
+ export function param(
1739
+ name: string,
1740
+ options?: { description?: string; schema?: OpenAPISchema; example?: any }
1741
+ ): OpenAPIParameter;
1742
+
1743
+ /**
1744
+ * Helper to create query parameter specification
1745
+ * @param name - Parameter name
1746
+ * @param options - Parameter options
1747
+ */
1748
+ export function query(
1749
+ name: string,
1750
+ options?: { description?: string; required?: boolean; schema?: OpenAPISchema; example?: any }
1751
+ ): OpenAPIParameter;
1752
+
1753
+ /**
1754
+ * Helper to create header parameter specification
1755
+ * @param name - Header name
1756
+ * @param options - Parameter options
1757
+ */
1758
+ export function header(
1759
+ name: string,
1760
+ options?: { description?: string; required?: boolean; schema?: OpenAPISchema; example?: any }
1761
+ ): OpenAPIParameter;
1762
+
1503
1763
  //# sourceMappingURL=index.d.ts.map