@dyrected/core 2.5.30 → 2.5.32

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/dist/server.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import * as hono from 'hono';
3
3
  import { Hono, Context } from 'hono';
4
- import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './index-DTrpTru7.cjs';
4
+ import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './index-CdQwnbfh.cjs';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
6
6
  import * as hono_utils_types from 'hono/utils/types';
7
7
  import 'lucide-react';
@@ -217,7 +217,12 @@ declare class CollectionController {
217
217
  message: string;
218
218
  }, 500, "json">) | (Response & hono.TypedResponse<{
219
219
  message: string;
220
- }, 404, "json">)>;
220
+ }, 404, "json">) | (Response & hono.TypedResponse<{
221
+ message: string;
222
+ }, 400, "json">) | (Response & hono.TypedResponse<{
223
+ error: true;
224
+ message: string;
225
+ }, 403, "json">)>;
221
226
  /**
222
227
  * POST /api/collections/:slug/:id/change-password
223
228
  *
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import * as hono from 'hono';
3
3
  import { Hono, Context } from 'hono';
4
- import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './index-DTrpTru7.js';
4
+ import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './index-CdQwnbfh.js';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
6
6
  import * as hono_utils_types from 'hono/utils/types';
7
7
  import 'lucide-react';
@@ -217,7 +217,12 @@ declare class CollectionController {
217
217
  message: string;
218
218
  }, 500, "json">) | (Response & hono.TypedResponse<{
219
219
  message: string;
220
- }, 404, "json">)>;
220
+ }, 404, "json">) | (Response & hono.TypedResponse<{
221
+ message: string;
222
+ }, 400, "json">) | (Response & hono.TypedResponse<{
223
+ error: true;
224
+ message: string;
225
+ }, 403, "json">)>;
221
226
  /**
222
227
  * POST /api/collections/:slug/:id/change-password
223
228
  *
package/dist/server.js CHANGED
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  WORKFLOW_HISTORY_COLLECTION,
3
+ canViewWorkflowDraft,
3
4
  createWorkflowDocument,
4
5
  executeFieldAfterRead,
5
6
  executeFieldBeforeChange,
7
+ generateOpenApi,
6
8
  initializeWorkflowDocument,
7
9
  materializeWorkflowDocument,
8
10
  normalizeConfig,
9
11
  runCollectionHooks,
10
12
  saveWorkflowDraft,
11
13
  transitionWorkflow
12
- } from "./chunk-CQDVPMEU.js";
14
+ } from "./chunk-YDOBB7MY.js";
13
15
 
14
16
  // src/app.ts
15
17
  import { Hono } from "hono";
@@ -282,6 +284,20 @@ function createReadonlyDb(db) {
282
284
  });
283
285
  }
284
286
 
287
+ // src/auth/jexl.ts
288
+ import jexl from "jexl";
289
+ async function evaluateAccess(expression, context) {
290
+ if (expression === void 0 || expression === null) return false;
291
+ if (typeof expression === "boolean") return expression;
292
+ try {
293
+ const result = await jexl.eval(expression, context);
294
+ return !!result;
295
+ } catch (err) {
296
+ console.error("[dyrected/core] Jexl evaluation failed:", err);
297
+ return false;
298
+ }
299
+ }
300
+
285
301
  // src/controllers/collection.controller.ts
286
302
  var CollectionController = class {
287
303
  collection;
@@ -326,7 +342,7 @@ var CollectionController = class {
326
342
  if (beforeReadResult !== void 0) {
327
343
  where = beforeReadResult;
328
344
  }
329
- if (this.collection.workflow && !user) {
345
+ if (this.collection.workflow && !canViewWorkflowDraft(this.collection.workflow, user)) {
330
346
  where = where ? { AND: [where, { __published: { exists: true } }] } : { __published: { exists: true } };
331
347
  }
332
348
  let result = await db.find({
@@ -619,9 +635,28 @@ var CollectionController = class {
619
635
  const config = c.get("config");
620
636
  if (!config.db) return c.json({ message: "Database not configured" }, 500);
621
637
  if (!this.collection.workflow) return c.json({ message: "Workflows are not enabled for this collection" }, 404);
638
+ const documentId = c.req.param("id");
639
+ if (!documentId) return c.json({ message: "Missing ID" }, 400);
640
+ const document = await config.db.findOne({ collection: this.collection.slug, id: documentId });
641
+ if (!document) return c.json({ message: "Not Found" }, 404);
642
+ const readAccess = this.collection.access?.read;
643
+ if (readAccess !== void 0 && readAccess !== null) {
644
+ const args = { user: c.get("user"), req: c.req, doc: document };
645
+ const result2 = typeof readAccess === "function" ? await readAccess(args) : await evaluateAccess(readAccess, args);
646
+ let allowed = result2 === true;
647
+ if (result2 && typeof result2 === "object") {
648
+ const match = await config.db.find({
649
+ collection: this.collection.slug,
650
+ where: { AND: [{ id: { equals: documentId } }, result2] },
651
+ limit: 1
652
+ });
653
+ allowed = match.total > 0;
654
+ }
655
+ if (!allowed) return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
656
+ }
622
657
  const result = await config.db.find({
623
658
  collection: WORKFLOW_HISTORY_COLLECTION,
624
- where: { collection: { equals: this.collection.slug }, documentId: { equals: c.req.param("id") } },
659
+ where: { collection: { equals: this.collection.slug }, documentId: { equals: documentId } },
625
660
  sort: "-createdAt",
626
661
  limit: Math.min(Number(c.req.query("limit")) || 50, 100)
627
662
  });
@@ -1643,328 +1678,6 @@ function optionalAuth() {
1643
1678
  };
1644
1679
  }
1645
1680
 
1646
- // src/utils/openapi.ts
1647
- function generateOpenApi(config) {
1648
- const spec = {
1649
- openapi: "3.0.0",
1650
- info: {
1651
- title: "Dyrected API",
1652
- version: "1.0.0",
1653
- description: "Automatically generated OpenAPI specification for the Dyrected project."
1654
- },
1655
- components: {
1656
- schemas: {},
1657
- securitySchemes: {
1658
- ApiKeyAuth: {
1659
- type: "apiKey",
1660
- in: "header",
1661
- name: "x-api-key"
1662
- }
1663
- }
1664
- },
1665
- paths: {},
1666
- security: [{ ApiKeyAuth: [] }]
1667
- };
1668
- for (const collection of config.collections) {
1669
- spec.components.schemas[collection.slug] = collectionToSchema(collection);
1670
- }
1671
- for (const global of config.globals) {
1672
- spec.components.schemas[global.slug] = globalToSchema(global);
1673
- }
1674
- for (const collection of config.collections) {
1675
- const slug = collection.slug;
1676
- const path = `/api/collections/${slug}`;
1677
- const labels = collection.labels || { singular: slug, plural: `${slug}s` };
1678
- spec.paths[path] = {
1679
- get: {
1680
- tags: ["Collections"],
1681
- summary: `Find ${labels.plural}`,
1682
- parameters: [
1683
- { name: "limit", in: "query", schema: { type: "integer", default: 10 } },
1684
- { name: "page", in: "query", schema: { type: "integer", default: 1 } },
1685
- { name: "where", in: "query", schema: { type: "string" }, description: "JSON filter" },
1686
- { name: "sort", in: "query", schema: { type: "string" }, description: "Sort field (e.g. -createdAt)" }
1687
- ],
1688
- responses: {
1689
- 200: {
1690
- description: "Success",
1691
- content: {
1692
- "application/json": {
1693
- schema: {
1694
- type: "object",
1695
- properties: {
1696
- docs: { type: "array", items: { $ref: `#/components/schemas/${slug}` } },
1697
- total: { type: "integer" },
1698
- limit: { type: "integer" },
1699
- page: { type: "integer" }
1700
- }
1701
- }
1702
- }
1703
- }
1704
- }
1705
- }
1706
- },
1707
- post: {
1708
- tags: ["Collections"],
1709
- summary: `Create ${labels.singular}`,
1710
- requestBody: {
1711
- required: true,
1712
- content: {
1713
- "application/json": {
1714
- schema: { $ref: `#/components/schemas/${slug}` }
1715
- }
1716
- }
1717
- },
1718
- responses: {
1719
- 201: {
1720
- description: "Created",
1721
- content: {
1722
- "application/json": {
1723
- schema: { $ref: `#/components/schemas/${slug}` }
1724
- }
1725
- }
1726
- }
1727
- }
1728
- }
1729
- };
1730
- spec.paths[`${path}/{id}`] = {
1731
- get: {
1732
- tags: ["Collections"],
1733
- summary: `Get a single ${labels.singular}`,
1734
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
1735
- responses: {
1736
- 200: {
1737
- description: "Success",
1738
- content: {
1739
- "application/json": {
1740
- schema: { $ref: `#/components/schemas/${slug}` }
1741
- }
1742
- }
1743
- }
1744
- }
1745
- },
1746
- patch: {
1747
- tags: ["Collections"],
1748
- summary: `Update ${labels.singular}`,
1749
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
1750
- requestBody: {
1751
- required: true,
1752
- content: {
1753
- "application/json": {
1754
- schema: { $ref: `#/components/schemas/${slug}` }
1755
- }
1756
- }
1757
- },
1758
- responses: {
1759
- 200: {
1760
- description: "Updated",
1761
- content: {
1762
- "application/json": {
1763
- schema: { $ref: `#/components/schemas/${slug}` }
1764
- }
1765
- }
1766
- }
1767
- }
1768
- },
1769
- delete: {
1770
- tags: ["Collections"],
1771
- summary: `Delete ${labels.singular}`,
1772
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
1773
- responses: {
1774
- 204: { description: "Deleted" }
1775
- }
1776
- }
1777
- };
1778
- }
1779
- for (const global of config.globals) {
1780
- const slug = global.slug;
1781
- const path = `/api/globals/${slug}`;
1782
- spec.paths[path] = {
1783
- get: {
1784
- tags: ["Globals"],
1785
- summary: `Get ${global.label || slug}`,
1786
- responses: {
1787
- 200: {
1788
- description: "Success",
1789
- content: {
1790
- "application/json": {
1791
- schema: { $ref: `#/components/schemas/${slug}` }
1792
- }
1793
- }
1794
- }
1795
- }
1796
- },
1797
- patch: {
1798
- tags: ["Globals"],
1799
- summary: `Update ${global.label || slug}`,
1800
- requestBody: {
1801
- required: true,
1802
- content: {
1803
- "application/json": {
1804
- schema: { $ref: `#/components/schemas/${slug}` }
1805
- }
1806
- }
1807
- },
1808
- responses: {
1809
- 200: {
1810
- description: "Updated",
1811
- content: {
1812
- "application/json": {
1813
- schema: { $ref: `#/components/schemas/${slug}` }
1814
- }
1815
- }
1816
- }
1817
- }
1818
- }
1819
- };
1820
- }
1821
- if (config.storage) {
1822
- spec.paths["/api/media"] = {
1823
- get: {
1824
- tags: ["Media"],
1825
- summary: "List Media",
1826
- responses: {
1827
- 200: {
1828
- description: "Success",
1829
- content: {
1830
- "application/json": {
1831
- schema: {
1832
- type: "object",
1833
- properties: {
1834
- docs: { type: "array", items: { type: "object", additionalProperties: true } }
1835
- }
1836
- }
1837
- }
1838
- }
1839
- }
1840
- }
1841
- },
1842
- post: {
1843
- tags: ["Media"],
1844
- summary: "Upload Media",
1845
- requestBody: {
1846
- content: {
1847
- "multipart/form-data": {
1848
- schema: {
1849
- type: "object",
1850
- properties: {
1851
- file: { type: "string", format: "binary" }
1852
- }
1853
- }
1854
- }
1855
- }
1856
- },
1857
- responses: {
1858
- 201: { description: "Uploaded" }
1859
- }
1860
- }
1861
- };
1862
- }
1863
- return spec;
1864
- }
1865
- function collectionToSchema(collection) {
1866
- const { properties, required } = fieldsToProperties(collection.fields);
1867
- return {
1868
- type: "object",
1869
- properties: {
1870
- id: { type: "string" },
1871
- createdAt: { type: "string", format: "date-time" },
1872
- updatedAt: { type: "string", format: "date-time" },
1873
- ...properties
1874
- },
1875
- required: ["id", ...required]
1876
- };
1877
- }
1878
- function globalToSchema(global) {
1879
- const { properties, required } = fieldsToProperties(global.fields);
1880
- return {
1881
- type: "object",
1882
- properties,
1883
- required
1884
- };
1885
- }
1886
- function fieldsToProperties(fields) {
1887
- const props = {};
1888
- const required = [];
1889
- for (const field of fields) {
1890
- if (!field.name || field.type === "join" || field.type === "row") continue;
1891
- props[field.name] = fieldToSchema(field);
1892
- if (field.required) {
1893
- required.push(field.name);
1894
- }
1895
- }
1896
- return { properties: props, required };
1897
- }
1898
- function fieldToSchema(field) {
1899
- let schema = {};
1900
- switch (field.type) {
1901
- case "text":
1902
- case "textarea":
1903
- case "email":
1904
- case "url":
1905
- schema = { type: "string" };
1906
- break;
1907
- case "number":
1908
- schema = { type: "number" };
1909
- break;
1910
- case "boolean":
1911
- schema = { type: "boolean" };
1912
- break;
1913
- case "date":
1914
- schema = { type: "string", format: "date-time" };
1915
- break;
1916
- case "select":
1917
- case "radio":
1918
- schema = { type: "string", enum: Array.isArray(field.options) ? field.options.map((o) => typeof o === "string" ? o : o.value) : void 0 };
1919
- break;
1920
- case "multiSelect":
1921
- schema = {
1922
- type: "array",
1923
- items: { type: "string", enum: Array.isArray(field.options) ? field.options.map((o) => typeof o === "string" ? o : o.value) : void 0 }
1924
- };
1925
- break;
1926
- case "relationship":
1927
- schema = { type: "string", description: `ID of a ${field.relationTo} record` };
1928
- break;
1929
- case "object": {
1930
- const { properties, required } = fieldsToProperties(field.fields || []);
1931
- schema = { type: "object", properties, required };
1932
- break;
1933
- }
1934
- case "array": {
1935
- const { properties, required } = fieldsToProperties(field.fields || []);
1936
- schema = { type: "array", items: { type: "object", properties, required } };
1937
- break;
1938
- }
1939
- case "json":
1940
- case "richText":
1941
- schema = { type: "object", additionalProperties: true };
1942
- break;
1943
- case "blocks":
1944
- schema = {
1945
- type: "array",
1946
- items: {
1947
- oneOf: field.blocks?.map((block) => {
1948
- const { properties, required } = fieldsToProperties(block.fields);
1949
- return {
1950
- type: "object",
1951
- properties: {
1952
- blockType: { type: "string", enum: [block.slug] },
1953
- ...properties
1954
- },
1955
- required: ["blockType", ...required]
1956
- };
1957
- })
1958
- }
1959
- };
1960
- break;
1961
- default:
1962
- schema = { type: "string" };
1963
- }
1964
- if (field.label) schema.description = field.label;
1965
- return schema;
1966
- }
1967
-
1968
1681
  // src/utils/swagger.ts
1969
1682
  function getSwaggerHtml(specUrl = "/api/openapi.json") {
1970
1683
  return `
@@ -2014,20 +1727,6 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
2014
1727
  `;
2015
1728
  }
2016
1729
 
2017
- // src/auth/jexl.ts
2018
- import jexl from "jexl";
2019
- async function evaluateAccess(expression, context) {
2020
- if (expression === void 0 || expression === null) return false;
2021
- if (typeof expression === "boolean") return expression;
2022
- try {
2023
- const result = await jexl.eval(expression, context);
2024
- return !!result;
2025
- } catch (err) {
2026
- console.error("[dyrected/core] Jexl evaluation failed:", err);
2027
- return false;
2028
- }
2029
- }
2030
-
2031
1730
  // src/router.ts
2032
1731
  function accessGate(target, action) {
2033
1732
  return async (c, next) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/core",
3
- "version": "2.5.30",
3
+ "version": "2.5.32",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",