@ketvietlab/ketsuite 0.1.3 → 0.1.6
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/cli.js +1 -1
- package/dist/deployment.d.ts.map +1 -1
- package/dist/deployment.js +3 -0
- package/dist/deployment.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/account_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/account_staff_channel/channel-routes.js +1 -12
- package/dist/modules/account_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/attendance/channel-routes.d.ts.map +1 -1
- package/dist/modules/attendance/channel-routes.js +1 -12
- package/dist/modules/attendance/channel-routes.js.map +1 -1
- package/dist/modules/backend/design/HANDOFF.md +2 -2
- package/dist/modules/backend/design/auth.css +12 -0
- package/dist/modules/backend/design/client/table-selection.d.mts +3 -3
- package/dist/modules/backend/design/client/table-selection.d.mts.map +1 -1
- package/dist/modules/backend/design/client/table-selection.mjs +1 -1
- package/dist/modules/backend/design/client/table-selection.mjs.map +4 -4
- package/dist/modules/backend/design/content.css +43 -0
- package/dist/modules/backend/design/design-system.css +1 -1
- package/dist/modules/backend/design/forms.css +84 -1
- package/dist/modules/backend/design/lists.css +26 -18
- package/dist/modules/backend/design/record.css +5 -2
- package/dist/modules/backend/design/responsive.css +5 -6
- package/dist/modules/backend/design/user-workflow.css +257 -0
- package/dist/modules/backend/forms.d.ts +38 -1
- package/dist/modules/backend/forms.d.ts.map +1 -1
- package/dist/modules/backend/forms.js +29 -0
- package/dist/modules/backend/forms.js.map +1 -1
- package/dist/modules/backend/index.d.ts +3 -2
- package/dist/modules/backend/index.d.ts.map +1 -1
- package/dist/modules/backend/index.js +3 -2
- package/dist/modules/backend/index.js.map +1 -1
- package/dist/modules/backend/messages.d.ts.map +1 -1
- package/dist/modules/backend/messages.js +42 -2
- package/dist/modules/backend/messages.js.map +1 -1
- package/dist/modules/channel_api/core.d.ts +30 -0
- package/dist/modules/channel_api/core.d.ts.map +1 -1
- package/dist/modules/channel_api/core.js +28 -0
- package/dist/modules/channel_api/core.js.map +1 -1
- package/dist/modules/crm/functions.d.ts +5 -0
- package/dist/modules/crm/functions.d.ts.map +1 -1
- package/dist/modules/crm/functions.js +105 -10
- package/dist/modules/crm/functions.js.map +1 -1
- package/dist/modules/crm/jobs.d.ts.map +1 -1
- package/dist/modules/crm/jobs.js +1 -0
- package/dist/modules/crm/jobs.js.map +1 -1
- package/dist/modules/crm/operations.d.ts.map +1 -1
- package/dist/modules/crm/operations.js +2 -1
- package/dist/modules/crm/operations.js.map +1 -1
- package/dist/modules/crm_backend/index.d.ts.map +1 -1
- package/dist/modules/crm_backend/index.js +5 -2
- package/dist/modules/crm_backend/index.js.map +1 -1
- package/dist/modules/crm_backend/messages.d.ts +116 -0
- package/dist/modules/crm_backend/messages.d.ts.map +1 -1
- package/dist/modules/crm_backend/messages.js +122 -6
- package/dist/modules/crm_backend/messages.js.map +1 -1
- package/dist/modules/crm_backend/routes.d.ts.map +1 -1
- package/dist/modules/crm_backend/routes.js +434 -174
- package/dist/modules/crm_backend/routes.js.map +1 -1
- package/dist/modules/crm_backend/screens/case-create.d.ts +7 -0
- package/dist/modules/crm_backend/screens/case-create.d.ts.map +1 -1
- package/dist/modules/crm_backend/screens/case-create.js +13 -4
- package/dist/modules/crm_backend/screens/case-create.js.map +1 -1
- package/dist/modules/crm_backend/screens/case-detail.d.ts +6 -0
- package/dist/modules/crm_backend/screens/case-detail.d.ts.map +1 -1
- package/dist/modules/crm_backend/screens/case-detail.js +73 -23
- package/dist/modules/crm_backend/screens/case-detail.js.map +1 -1
- package/dist/modules/crm_backend/screens/configuration.d.ts +21 -3
- package/dist/modules/crm_backend/screens/configuration.d.ts.map +1 -1
- package/dist/modules/crm_backend/screens/configuration.js +260 -55
- package/dist/modules/crm_backend/screens/configuration.js.map +1 -1
- package/dist/modules/crm_backend/screens/index.d.ts +3 -3
- package/dist/modules/crm_backend/screens/index.d.ts.map +1 -1
- package/dist/modules/crm_backend/screens/index.js +2 -2
- package/dist/modules/crm_backend/screens/index.js.map +1 -1
- package/dist/modules/crm_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/crm_staff_channel/channel-routes.js +1 -12
- package/dist/modules/crm_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/flow/membership.d.ts.map +1 -1
- package/dist/modules/flow/membership.js +7 -1
- package/dist/modules/flow/membership.js.map +1 -1
- package/dist/modules/flow/operations.d.ts +10 -4
- package/dist/modules/flow/operations.d.ts.map +1 -1
- package/dist/modules/flow/operations.js +10 -4
- package/dist/modules/flow/operations.js.map +1 -1
- package/dist/modules/flow_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/flow_staff_channel/channel-routes.js +1 -20
- package/dist/modules/flow_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/hospitality_core/messages.d.ts.map +1 -1
- package/dist/modules/hospitality_core/messages.js +44 -0
- package/dist/modules/hospitality_core/messages.js.map +1 -1
- package/dist/modules/hospitality_core/night-audit.d.ts.map +1 -1
- package/dist/modules/hospitality_core/night-audit.js +7 -0
- package/dist/modules/hospitality_core/night-audit.js.map +1 -1
- package/dist/modules/hospitality_core/operations.d.ts.map +1 -1
- package/dist/modules/hospitality_core/operations.js +150 -40
- package/dist/modules/hospitality_core/operations.js.map +1 -1
- package/dist/modules/hospitality_core/routes.d.ts.map +1 -1
- package/dist/modules/hospitality_core/routes.js +24 -2
- package/dist/modules/hospitality_core/routes.js.map +1 -1
- package/dist/modules/hospitality_core/screens/check-out-prep.d.ts +46 -0
- package/dist/modules/hospitality_core/screens/check-out-prep.d.ts.map +1 -0
- package/dist/modules/hospitality_core/screens/check-out-prep.js +133 -0
- package/dist/modules/hospitality_core/screens/check-out-prep.js.map +1 -0
- package/dist/modules/hospitality_core/screens/folio-detail.d.ts.map +1 -1
- package/dist/modules/hospitality_core/screens/folio-detail.js +2 -4
- package/dist/modules/hospitality_core/screens/folio-detail.js.map +1 -1
- package/dist/modules/hospitality_core/screens/index.d.ts +2 -0
- package/dist/modules/hospitality_core/screens/index.d.ts.map +1 -1
- package/dist/modules/hospitality_core/screens/index.js +1 -0
- package/dist/modules/hospitality_core/screens/index.js.map +1 -1
- package/dist/modules/hospitality_core/screens/reservation-detail.d.ts.map +1 -1
- package/dist/modules/hospitality_core/screens/reservation-detail.js +8 -1
- package/dist/modules/hospitality_core/screens/reservation-detail.js.map +1 -1
- package/dist/modules/hospitality_core/screens/shared.d.ts +12 -0
- package/dist/modules/hospitality_core/screens/shared.d.ts.map +1 -1
- package/dist/modules/hospitality_core/screens/shared.js +25 -4
- package/dist/modules/hospitality_core/screens/shared.js.map +1 -1
- package/dist/modules/hospitality_core/services.d.ts +48 -0
- package/dist/modules/hospitality_core/services.d.ts.map +1 -1
- package/dist/modules/hospitality_core/services.js +70 -0
- package/dist/modules/hospitality_core/services.js.map +1 -1
- package/dist/modules/hospitality_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/hospitality_staff_channel/channel-routes.js +1 -12
- package/dist/modules/hospitality_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/inventory_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/inventory_staff_channel/channel-routes.js +1 -12
- package/dist/modules/inventory_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/loyalty/admin-functions.d.ts +2 -0
- package/dist/modules/loyalty/admin-functions.d.ts.map +1 -1
- package/dist/modules/loyalty/admin-functions.js +96 -20
- package/dist/modules/loyalty/admin-functions.js.map +1 -1
- package/dist/modules/loyalty/engine.d.ts +4 -1
- package/dist/modules/loyalty/engine.d.ts.map +1 -1
- package/dist/modules/loyalty/engine.js +125 -18
- package/dist/modules/loyalty/engine.js.map +1 -1
- package/dist/modules/loyalty/index.d.ts.map +1 -1
- package/dist/modules/loyalty/index.js +4 -3
- package/dist/modules/loyalty/index.js.map +1 -1
- package/dist/modules/loyalty/jobs.d.ts.map +1 -1
- package/dist/modules/loyalty/jobs.js +0 -1
- package/dist/modules/loyalty/jobs.js.map +1 -1
- package/dist/modules/loyalty/membership-functions.d.ts +5 -0
- package/dist/modules/loyalty/membership-functions.d.ts.map +1 -1
- package/dist/modules/loyalty/membership-functions.js +37 -23
- package/dist/modules/loyalty/membership-functions.js.map +1 -1
- package/dist/modules/loyalty/messages.d.ts +14 -0
- package/dist/modules/loyalty/messages.d.ts.map +1 -1
- package/dist/modules/loyalty/messages.js +14 -0
- package/dist/modules/loyalty/messages.js.map +1 -1
- package/dist/modules/loyalty/models.d.ts.map +1 -1
- package/dist/modules/loyalty/models.js +17 -0
- package/dist/modules/loyalty/models.js.map +1 -1
- package/dist/modules/loyalty/order-functions.d.ts.map +1 -1
- package/dist/modules/loyalty/order-functions.js +70 -17
- package/dist/modules/loyalty/order-functions.js.map +1 -1
- package/dist/modules/loyalty/program-design.d.ts +11 -0
- package/dist/modules/loyalty/program-design.d.ts.map +1 -0
- package/dist/modules/loyalty/program-design.js +356 -0
- package/dist/modules/loyalty/program-design.js.map +1 -0
- package/dist/modules/loyalty/stats.d.ts +0 -13
- package/dist/modules/loyalty/stats.d.ts.map +1 -1
- package/dist/modules/loyalty/stats.js +11 -6
- package/dist/modules/loyalty/stats.js.map +1 -1
- package/dist/modules/loyalty/stored-value.d.ts.map +1 -1
- package/dist/modules/loyalty/stored-value.js +7 -2
- package/dist/modules/loyalty/stored-value.js.map +1 -1
- package/dist/modules/loyalty/types.d.ts +1 -1
- package/dist/modules/loyalty/types.d.ts.map +1 -1
- package/dist/modules/loyalty_backend/design-messages.d.ts +221 -0
- package/dist/modules/loyalty_backend/design-messages.d.ts.map +1 -0
- package/dist/modules/loyalty_backend/design-messages.js +221 -0
- package/dist/modules/loyalty_backend/design-messages.js.map +1 -0
- package/dist/modules/loyalty_backend/index.js +224 -412
- package/dist/modules/loyalty_backend/index.js.map +1 -1
- package/dist/modules/loyalty_backend/messages.d.ts +642 -214
- package/dist/modules/loyalty_backend/messages.d.ts.map +1 -1
- package/dist/modules/loyalty_backend/messages.js +19 -13
- package/dist/modules/loyalty_backend/messages.js.map +1 -1
- package/dist/modules/loyalty_backend/program-workspace.d.ts +4 -0
- package/dist/modules/loyalty_backend/program-workspace.d.ts.map +1 -0
- package/dist/modules/loyalty_backend/program-workspace.js +600 -0
- package/dist/modules/loyalty_backend/program-workspace.js.map +1 -0
- package/dist/modules/loyalty_backend/screens/index.d.ts +12 -36
- package/dist/modules/loyalty_backend/screens/index.d.ts.map +1 -1
- package/dist/modules/loyalty_backend/screens/index.js +148 -292
- package/dist/modules/loyalty_backend/screens/index.js.map +1 -1
- package/dist/modules/loyalty_backend/screens/page-frame.d.ts.map +1 -1
- package/dist/modules/loyalty_backend/screens/page-frame.js +2 -2
- package/dist/modules/loyalty_backend/screens/page-frame.js.map +1 -1
- package/dist/modules/loyalty_pos/functions.d.ts.map +1 -1
- package/dist/modules/loyalty_pos/functions.js +1 -0
- package/dist/modules/loyalty_pos/functions.js.map +1 -1
- package/dist/modules/loyalty_pos/index.js +1 -1
- package/dist/modules/loyalty_pos/index.js.map +1 -1
- package/dist/modules/loyalty_sale/functions.d.ts +1 -0
- package/dist/modules/loyalty_sale/functions.d.ts.map +1 -1
- package/dist/modules/loyalty_sale/functions.js +1 -0
- package/dist/modules/loyalty_sale/functions.js.map +1 -1
- package/dist/modules/partner_backend/client/partner.css +3 -1
- package/dist/modules/partner_backend/index.d.ts.map +1 -1
- package/dist/modules/partner_backend/index.js +4 -0
- package/dist/modules/partner_backend/index.js.map +1 -1
- package/dist/modules/partner_backend/routes.d.ts.map +1 -1
- package/dist/modules/partner_backend/routes.js +6 -1
- package/dist/modules/partner_backend/routes.js.map +1 -1
- package/dist/modules/partner_backend/screens/form.d.ts +1 -0
- package/dist/modules/partner_backend/screens/form.d.ts.map +1 -1
- package/dist/modules/partner_backend/screens/form.js +1 -0
- package/dist/modules/partner_backend/screens/form.js.map +1 -1
- package/dist/modules/purchase_staff_channel/order-routes.d.ts.map +1 -1
- package/dist/modules/purchase_staff_channel/order-routes.js +1 -12
- package/dist/modules/purchase_staff_channel/order-routes.js.map +1 -1
- package/dist/modules/quality_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/quality_staff_channel/channel-routes.js +1 -12
- package/dist/modules/quality_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/sale/functions.d.ts.map +1 -1
- package/dist/modules/sale/functions.js +27 -17
- package/dist/modules/sale/functions.js.map +1 -1
- package/dist/modules/sale_staff_channel/channel-routes.d.ts.map +1 -1
- package/dist/modules/sale_staff_channel/channel-routes.js +1 -12
- package/dist/modules/sale_staff_channel/channel-routes.js.map +1 -1
- package/dist/modules/sale_staff_channel/order-routes.d.ts.map +1 -1
- package/dist/modules/sale_staff_channel/order-routes.js +1 -12
- package/dist/modules/sale_staff_channel/order-routes.js.map +1 -1
- package/dist/modules/stock_staff_channel/operation-routes.d.ts.map +1 -1
- package/dist/modules/stock_staff_channel/operation-routes.js +1 -12
- package/dist/modules/stock_staff_channel/operation-routes.js.map +1 -1
- package/dist/modules/user/authorization.d.ts +64 -1
- package/dist/modules/user/authorization.d.ts.map +1 -1
- package/dist/modules/user/authorization.js +445 -53
- package/dist/modules/user/authorization.js.map +1 -1
- package/dist/modules/user/functions.d.ts.map +1 -1
- package/dist/modules/user/functions.js +45 -23
- package/dist/modules/user/functions.js.map +1 -1
- package/dist/modules/user/index.d.ts +2 -1
- package/dist/modules/user/index.d.ts.map +1 -1
- package/dist/modules/user/index.js +1 -1
- package/dist/modules/user/index.js.map +1 -1
- package/dist/modules/user/models.d.ts.map +1 -1
- package/dist/modules/user/models.js +5 -0
- package/dist/modules/user/models.js.map +1 -1
- package/dist/modules/user/roles.d.ts.map +1 -1
- package/dist/modules/user/roles.js +2 -1
- package/dist/modules/user/roles.js.map +1 -1
- package/dist/modules/user_backend/access-routes.d.ts +31 -0
- package/dist/modules/user_backend/access-routes.d.ts.map +1 -0
- package/dist/modules/user_backend/access-routes.js +447 -0
- package/dist/modules/user_backend/access-routes.js.map +1 -0
- package/dist/modules/user_backend/index.d.ts +1 -0
- package/dist/modules/user_backend/index.d.ts.map +1 -1
- package/dist/modules/user_backend/index.js +1 -0
- package/dist/modules/user_backend/index.js.map +1 -1
- package/dist/modules/user_backend/messages.d.ts +4 -0
- package/dist/modules/user_backend/messages.d.ts.map +1 -1
- package/dist/modules/user_backend/messages.js +4 -0
- package/dist/modules/user_backend/messages.js.map +1 -1
- package/dist/modules/user_backend/routes.d.ts.map +1 -1
- package/dist/modules/user_backend/routes.js +38 -3
- package/dist/modules/user_backend/routes.js.map +1 -1
- package/dist/modules/user_backend/screens/user-form.d.ts +1 -0
- package/dist/modules/user_backend/screens/user-form.d.ts.map +1 -1
- package/dist/modules/user_backend/screens/user-form.js +28 -0
- package/dist/modules/user_backend/screens/user-form.js.map +1 -1
- package/dist/modules/website/cms.d.ts.map +1 -1
- package/dist/modules/website/cms.js +283 -6
- package/dist/modules/website/cms.js.map +1 -1
- package/dist/modules/website/index.d.ts.map +1 -1
- package/dist/modules/website/index.js +12 -0
- package/dist/modules/website/index.js.map +1 -1
- package/dist/modules/website/media-usage.d.ts +38 -0
- package/dist/modules/website/media-usage.d.ts.map +1 -0
- package/dist/modules/website/media-usage.js +87 -0
- package/dist/modules/website/media-usage.js.map +1 -0
- package/dist/modules/website/paths.js +1 -1
- package/dist/modules/website/paths.js.map +1 -1
- package/dist/modules/website/sections.d.ts.map +1 -1
- package/dist/modules/website/sections.js +12 -1
- package/dist/modules/website/sections.js.map +1 -1
- package/dist/modules/website_backend/index.js +138 -0
- package/dist/modules/website_backend/index.js.map +1 -1
- package/dist/modules/website_backend/menus.d.ts.map +1 -1
- package/dist/modules/website_backend/menus.js +11 -0
- package/dist/modules/website_backend/menus.js.map +1 -1
- package/dist/modules/website_backend/routes.d.ts.map +1 -1
- package/dist/modules/website_backend/routes.js +414 -31
- package/dist/modules/website_backend/routes.js.map +1 -1
- package/dist/modules/website_backend/screens/index.d.ts +98 -6
- package/dist/modules/website_backend/screens/index.d.ts.map +1 -1
- package/dist/modules/website_backend/screens/index.js +600 -154
- package/dist/modules/website_backend/screens/index.js.map +1 -1
- package/dist/modules/website_backend/screens/page-frame.d.ts +3 -1
- package/dist/modules/website_backend/screens/page-frame.d.ts.map +1 -1
- package/dist/modules/website_backend/screens/page-frame.js +1 -1
- package/dist/modules/website_backend/screens/page-frame.js.map +1 -1
- package/dist/modules/website_menu/functions.d.ts.map +1 -1
- package/dist/modules/website_menu/functions.js +51 -0
- package/dist/modules/website_menu/functions.js.map +1 -1
- package/dist/modules/website_menu/index.js +2 -0
- package/dist/modules/website_menu/index.js.map +1 -1
- package/dist/modules/website_search/functions.d.ts.map +1 -1
- package/dist/modules/website_search/functions.js +10 -120
- package/dist/modules/website_search/functions.js.map +1 -1
- package/dist/modules/website_search/index.js +2 -0
- package/dist/modules/website_search/index.js.map +1 -1
- package/dist/modules/website_search/jobs.d.ts +3 -0
- package/dist/modules/website_search/jobs.d.ts.map +1 -0
- package/dist/modules/website_search/jobs.js +101 -0
- package/dist/modules/website_search/jobs.js.map +1 -0
- package/dist/modules/website_search/rebuild.d.ts +33 -0
- package/dist/modules/website_search/rebuild.d.ts.map +1 -0
- package/dist/modules/website_search/rebuild.js +125 -0
- package/dist/modules/website_search/rebuild.js.map +1 -0
- package/dist/permission-catalogue.d.ts.map +1 -1
- package/dist/permission-catalogue.js +21 -1
- package/dist/permission-catalogue.js.map +1 -1
- package/dist/scaffold/index.js +1 -1
- package/dist/ui/chrome.d.ts +8 -0
- package/dist/ui/chrome.d.ts.map +1 -1
- package/dist/ui/chrome.js +9 -1
- package/dist/ui/chrome.js.map +1 -1
- package/dist/ui/client/table-selection-view.d.ts.map +1 -1
- package/dist/ui/client/table-selection-view.js +161 -5
- package/dist/ui/client/table-selection-view.js.map +1 -1
- package/dist/ui/client/user-workflow.d.ts +2 -0
- package/dist/ui/client/user-workflow.d.ts.map +1 -0
- package/dist/ui/client/user-workflow.js +141 -0
- package/dist/ui/client/user-workflow.js.map +1 -0
- package/dist/ui/form.d.ts +9 -0
- package/dist/ui/form.d.ts.map +1 -1
- package/dist/ui/form.js +29 -15
- package/dist/ui/form.js.map +1 -1
- package/dist/ui/icons.d.ts.map +1 -1
- package/dist/ui/icons.js +2 -0
- package/dist/ui/icons.js.map +1 -1
- package/dist/ui/index.d.ts +7 -3
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +6 -3
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/layout.d.ts.map +1 -1
- package/dist/ui/layout.js +14 -4
- package/dist/ui/layout.js.map +1 -1
- package/dist/ui/nav.d.ts.map +1 -1
- package/dist/ui/nav.js +2 -1
- package/dist/ui/nav.js.map +1 -1
- package/dist/ui/state.d.ts +20 -1
- package/dist/ui/state.d.ts.map +1 -1
- package/dist/ui/state.js +17 -0
- package/dist/ui/state.js.map +1 -1
- package/dist/ui/table.js +1 -1
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/user-workflow.d.ts +72 -0
- package/dist/ui/user-workflow.d.ts.map +1 -0
- package/dist/ui/user-workflow.js +144 -0
- package/dist/ui/user-workflow.js.map +1 -0
- package/package.json +8 -4
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
-
import { asc, defineFn, deleteFrom, desc, eq, from, inArray, isNotNull, like, diffPlacements, isPlacementId, ne, placementIdErrors, validateLayout, withPlacementIds, } from '@ketvietlab/ketjs';
|
|
2
|
+
import { asc, defineFn, deleteFrom, desc, eq, from, inArray, isNotNull, like, likeLiteral, diffPlacements, isPlacementId, ne, partitionTokens, placementIdErrors, validateLayout, withPlacementIds, } from '@ketvietlab/ketjs';
|
|
3
3
|
import { canAccessSite, canAdministerSite, canCreateEntry, canEditEntry, canManageStructure, canPublishEntry, } from './access.js';
|
|
4
4
|
import { ensureCustomerRealm } from './customer.js';
|
|
5
5
|
import { isReservedPath, reservedPrefixes } from './paths.js';
|
|
6
|
+
import { usageOf } from './media-usage.js';
|
|
6
7
|
import { preflightEntry } from './renderable.js';
|
|
8
|
+
/**
|
|
9
|
+
* What a person typed in the title box, as a literal.
|
|
10
|
+
*
|
|
11
|
+
* Without the escape a `%` matched every page and a `_` matched any character,
|
|
12
|
+
* so the list answered nonsense to anyone searching for "50%" or "co_op" - and
|
|
13
|
+
* the count beside it agreed, which made it look deliberate.
|
|
14
|
+
*/
|
|
15
|
+
const titlePattern = (search) => `%${likeLiteral(String(search).trim().slice(0, 100))}%`;
|
|
7
16
|
const SITE_ROLES = new Set(['administrator', 'editor', 'author', 'contributor']);
|
|
8
17
|
/** How many pages one unnamed preflight will read. Beyond it, the answer is "ask again by id". */
|
|
9
18
|
const PREFLIGHT_SCAN_LIMIT = 1_000;
|
|
@@ -390,6 +399,14 @@ export const cmsFunctions = {
|
|
|
390
399
|
return invalid('defaultLocale', 'website.error.invalidLocale');
|
|
391
400
|
if (jsonBytes(args.tokens ?? {}) > 64 * 1024)
|
|
392
401
|
return invalid('tokens', 'website.error.payloadTooLarge');
|
|
402
|
+
const tokens = args.tokens;
|
|
403
|
+
if (tokens != null && (typeof tokens !== 'object' || Array.isArray(tokens)))
|
|
404
|
+
return invalid('tokens', 'website.error.invalidTokens');
|
|
405
|
+
if (tokens) {
|
|
406
|
+
const { rejected } = partitionTokens(tokens);
|
|
407
|
+
if (rejected.length)
|
|
408
|
+
return invalid('tokens', 'website.error.invalidTokenValue');
|
|
409
|
+
}
|
|
393
410
|
const duplicate = (await ctx.db.select('website.Site')).find((site) => site.id !== args.id && String(site.name).toLowerCase() === name.toLowerCase());
|
|
394
411
|
if (duplicate)
|
|
395
412
|
return invalid('name', 'website.error.duplicateName');
|
|
@@ -463,6 +480,28 @@ export const cmsFunctions = {
|
|
|
463
480
|
return { ok: true, id: args.id };
|
|
464
481
|
},
|
|
465
482
|
}),
|
|
483
|
+
deleteDomain: defineFn({
|
|
484
|
+
input: { id: 'id' },
|
|
485
|
+
output: { ok: 'bool', id: 'id?', errors: 'json?' },
|
|
486
|
+
effects: ['read:website.SiteMember', 'read:website.SiteDomain', 'write:website.SiteDomain'],
|
|
487
|
+
idempotent: true,
|
|
488
|
+
handler: async (ctx, args) => {
|
|
489
|
+
const domain = (await ctx.db.select('website.SiteDomain', { id: args.id }))[0];
|
|
490
|
+
if (!domain)
|
|
491
|
+
return { ok: true, id: args.id };
|
|
492
|
+
if (!(await canAdministerSite(ctx, domain.siteId)))
|
|
493
|
+
return forbidden();
|
|
494
|
+
const siblings = await ctx.db.select('website.SiteDomain', { siteId: domain.siteId });
|
|
495
|
+
// Canonical URLs and the sitemap are built from the primary, so removing
|
|
496
|
+
// it while other hosts still answer for the site would publish the wrong
|
|
497
|
+
// address to every crawler that asks. Promote another one first.
|
|
498
|
+
if (domain.primary === true && siblings.length > 1)
|
|
499
|
+
return invalid('id', 'website.error.primaryDomainInUse');
|
|
500
|
+
const Domain = ctx.table('website.SiteDomain');
|
|
501
|
+
await ctx.db.del(deleteFrom(Domain).where(eq(Domain.id, args.id)));
|
|
502
|
+
return { ok: true, id: args.id };
|
|
503
|
+
},
|
|
504
|
+
}),
|
|
466
505
|
listDomains: defineFn({
|
|
467
506
|
input: { siteId: 'id', limit: 'int?', offset: 'int?' },
|
|
468
507
|
output: {
|
|
@@ -572,10 +611,14 @@ export const cmsFunctions = {
|
|
|
572
611
|
.orderBy(desc(Entry.updatedAt), asc(Entry.title));
|
|
573
612
|
if (args.type)
|
|
574
613
|
query = query.where(eq(Entry.type, args.type));
|
|
614
|
+
// Asked for by name or left out entirely: a list of what there is does
|
|
615
|
+
// not mean a list of what was thrown away.
|
|
575
616
|
if (args.status)
|
|
576
617
|
query = query.where(eq(Entry.status, args.status));
|
|
618
|
+
else
|
|
619
|
+
query = query.where(ne(Entry.status, 'trash'));
|
|
577
620
|
if (args.search)
|
|
578
|
-
query = query.where(like(Entry.title,
|
|
621
|
+
query = query.where(like(Entry.title, titlePattern(args.search), true));
|
|
579
622
|
query = query.limit(paging.limit).offset(paging.offset);
|
|
580
623
|
return ctx.db.all(query);
|
|
581
624
|
},
|
|
@@ -591,10 +634,14 @@ export const cmsFunctions = {
|
|
|
591
634
|
let query = from(Entry).where(eq(Entry.siteId, args.siteId));
|
|
592
635
|
if (args.type)
|
|
593
636
|
query = query.where(eq(Entry.type, args.type));
|
|
637
|
+
// The same rule as listEntries, or the pager counts rows the list will
|
|
638
|
+
// not show and the last page comes back empty.
|
|
594
639
|
if (args.status)
|
|
595
640
|
query = query.where(eq(Entry.status, args.status));
|
|
641
|
+
else
|
|
642
|
+
query = query.where(ne(Entry.status, 'trash'));
|
|
596
643
|
if (args.search)
|
|
597
|
-
query = query.where(like(Entry.title,
|
|
644
|
+
query = query.where(like(Entry.title, titlePattern(args.search), true));
|
|
598
645
|
return { count: await ctx.db.count(query) };
|
|
599
646
|
},
|
|
600
647
|
}),
|
|
@@ -910,6 +957,105 @@ export const cmsFunctions = {
|
|
|
910
957
|
return { ok: true, id: args.id, status: 'published' };
|
|
911
958
|
},
|
|
912
959
|
}),
|
|
960
|
+
/**
|
|
961
|
+
* Out of the way, without being gone.
|
|
962
|
+
*
|
|
963
|
+
* Every consumer in the module already honours `trash`: the public resolver
|
|
964
|
+
* refuses it, the sitemap and the menu validator leave it out, the search
|
|
965
|
+
* index skips it, preflight and the media library do not count it, and
|
|
966
|
+
* `preparePublication` refuses a set containing one. Nothing could produce
|
|
967
|
+
* it, so a page created by mistake stayed in the list for ever.
|
|
968
|
+
*
|
|
969
|
+
* Trash rather than delete because deleting is the harder question and this
|
|
970
|
+
* is not it: `ref:` emits no foreign key, so removing an entry would leave
|
|
971
|
+
* its revisions, its term assignments, its preview tokens and - across a
|
|
972
|
+
* module boundary `website` cannot reach - its SEO row all pointing at
|
|
973
|
+
* nothing. Trash keeps every row and answers the question people actually
|
|
974
|
+
* have, which is "get this off my list".
|
|
975
|
+
*/
|
|
976
|
+
trashEntry: defineFn({
|
|
977
|
+
input: { id: 'id' },
|
|
978
|
+
output: { ok: 'bool', id: 'id?', errors: 'json?' },
|
|
979
|
+
effects: ['read:website.Entry', 'read:website.SiteMember', 'write:website.Entry'],
|
|
980
|
+
idempotent: true,
|
|
981
|
+
agent: true,
|
|
982
|
+
handler: async (ctx, args) => {
|
|
983
|
+
const entry = await entryById(ctx, args.id);
|
|
984
|
+
if (!entry || !(await canPublishEntry(ctx, entry)))
|
|
985
|
+
return forbidden();
|
|
986
|
+
if (entry.status === 'trash')
|
|
987
|
+
return { ok: true, id: args.id };
|
|
988
|
+
// The same clearing unpublishEntry does. The resolver checks the status
|
|
989
|
+
// too, but a pointer left behind is a pointer somebody later trusts.
|
|
990
|
+
await ctx.db.update('website.Entry', { id: args.id }, { status: 'trash', publishedRevisionId: null, scheduledRevisionId: null, publishAt: null });
|
|
991
|
+
return { ok: true, id: args.id };
|
|
992
|
+
},
|
|
993
|
+
}),
|
|
994
|
+
/**
|
|
995
|
+
* Back to a draft.
|
|
996
|
+
*
|
|
997
|
+
* `saveEntry` and `restoreRevision` both already revive a trashed entry,
|
|
998
|
+
* which is right - editing something means you want it back - but it is an
|
|
999
|
+
* implicit revival, and a screen needs a control that says what it does.
|
|
1000
|
+
*/
|
|
1001
|
+
untrashEntry: defineFn({
|
|
1002
|
+
input: { id: 'id' },
|
|
1003
|
+
output: { ok: 'bool', id: 'id?', errors: 'json?' },
|
|
1004
|
+
effects: ['read:website.Entry', 'read:website.SiteMember', 'write:website.Entry'],
|
|
1005
|
+
idempotent: true,
|
|
1006
|
+
agent: true,
|
|
1007
|
+
handler: async (ctx, args) => {
|
|
1008
|
+
const entry = await entryById(ctx, args.id);
|
|
1009
|
+
if (!entry || !(await canPublishEntry(ctx, entry)))
|
|
1010
|
+
return forbidden();
|
|
1011
|
+
if (entry.status !== 'trash')
|
|
1012
|
+
return { ok: true, id: args.id };
|
|
1013
|
+
// A draft, never straight back to published: what it used to say may be
|
|
1014
|
+
// the reason it was thrown away.
|
|
1015
|
+
await ctx.db.update('website.Entry', { id: args.id }, { status: 'draft' });
|
|
1016
|
+
return { ok: true, id: args.id };
|
|
1017
|
+
},
|
|
1018
|
+
}),
|
|
1019
|
+
/**
|
|
1020
|
+
* The inverse of publishEntry, which never had one.
|
|
1021
|
+
*
|
|
1022
|
+
* A page could go live and never come back down: nothing set `status` back,
|
|
1023
|
+
* and the public resolver's per-entry fallback reads `publishedRevisionId`,
|
|
1024
|
+
* which nothing ever cleared. A page published by mistake, an event that is
|
|
1025
|
+
* over, a takedown - all of them had only `noindex`, which asks crawlers to
|
|
1026
|
+
* forget the page while every visitor with the address still reads it.
|
|
1027
|
+
*
|
|
1028
|
+
* This is also how a schedule is cancelled: `publishScheduled` re-reads
|
|
1029
|
+
* `status` before it publishes, so a scheduled page moved back to draft
|
|
1030
|
+
* stays there and the queued job returns without doing anything.
|
|
1031
|
+
*
|
|
1032
|
+
* What it does not do is take a page out of an *active publication*. That
|
|
1033
|
+
* set is frozen by design, and the way to change what it contains is to
|
|
1034
|
+
* prepare and activate another one.
|
|
1035
|
+
*/
|
|
1036
|
+
unpublishEntry: defineFn({
|
|
1037
|
+
input: { id: 'id' },
|
|
1038
|
+
output: { ok: 'bool', id: 'id?', status: 'text?', errors: 'json?' },
|
|
1039
|
+
effects: ['read:website.Entry', 'read:website.SiteMember', 'write:website.Entry'],
|
|
1040
|
+
idempotent: true,
|
|
1041
|
+
agent: true,
|
|
1042
|
+
handler: async (ctx, args) => {
|
|
1043
|
+
const entry = await entryById(ctx, args.id);
|
|
1044
|
+
if (!entry || !(await canPublishEntry(ctx, entry)))
|
|
1045
|
+
return forbidden();
|
|
1046
|
+
if (entry.status === 'draft')
|
|
1047
|
+
return { ok: true, id: args.id, status: 'draft' };
|
|
1048
|
+
await ctx.db.update('website.Entry', { id: args.id }, {
|
|
1049
|
+
status: 'draft',
|
|
1050
|
+
publishedRevisionId: null,
|
|
1051
|
+
scheduledRevisionId: null,
|
|
1052
|
+
publishAt: null,
|
|
1053
|
+
});
|
|
1054
|
+
// publishedAt stays: it is the record of when the page was last live,
|
|
1055
|
+
// and clearing it would lose that to no purpose.
|
|
1056
|
+
return { ok: true, id: args.id, status: 'draft' };
|
|
1057
|
+
},
|
|
1058
|
+
}),
|
|
913
1059
|
/**
|
|
914
1060
|
* What would break if this went live now.
|
|
915
1061
|
*
|
|
@@ -1106,10 +1252,29 @@ export const cmsFunctions = {
|
|
|
1106
1252
|
return { token, expiresAt };
|
|
1107
1253
|
},
|
|
1108
1254
|
}),
|
|
1255
|
+
/**
|
|
1256
|
+
* The draft behind a preview link.
|
|
1257
|
+
*
|
|
1258
|
+
* Answers the same shape as `getEntryByPath`, because the storefront renders
|
|
1259
|
+
* whichever of the two answered and a second shape would mean a second
|
|
1260
|
+
* renderer. `meta` is the entry's own, not the publication's frozen copy: a
|
|
1261
|
+
* preview is for looking at what is about to go out.
|
|
1262
|
+
*/
|
|
1109
1263
|
previewEntry: defineFn({
|
|
1110
1264
|
anonymous: true,
|
|
1111
1265
|
input: { token: 'text' },
|
|
1112
|
-
output: {
|
|
1266
|
+
output: {
|
|
1267
|
+
id: 'id',
|
|
1268
|
+
siteId: 'id?',
|
|
1269
|
+
type: 'text?',
|
|
1270
|
+
path: 'text',
|
|
1271
|
+
title: 'text',
|
|
1272
|
+
excerpt: 'text?',
|
|
1273
|
+
layout: 'json',
|
|
1274
|
+
fields: 'json?',
|
|
1275
|
+
meta: 'json?',
|
|
1276
|
+
published: 'bool?',
|
|
1277
|
+
},
|
|
1113
1278
|
effects: [
|
|
1114
1279
|
'read:website.PreviewToken',
|
|
1115
1280
|
'write:website.PreviewToken',
|
|
@@ -1133,7 +1298,18 @@ export const cmsFunctions = {
|
|
|
1133
1298
|
if (!('dryRun' in used) && !used.matched)
|
|
1134
1299
|
return null;
|
|
1135
1300
|
}
|
|
1136
|
-
return {
|
|
1301
|
+
return {
|
|
1302
|
+
id: entry.id,
|
|
1303
|
+
siteId: entry.siteId,
|
|
1304
|
+
type: entry.type,
|
|
1305
|
+
path: entry.path,
|
|
1306
|
+
title: revision.title,
|
|
1307
|
+
excerpt: revision.excerpt ?? null,
|
|
1308
|
+
layout: revision.layout,
|
|
1309
|
+
fields: revision.fields,
|
|
1310
|
+
meta: publicMeta(entry),
|
|
1311
|
+
published: entry.status === 'published',
|
|
1312
|
+
};
|
|
1137
1313
|
},
|
|
1138
1314
|
}),
|
|
1139
1315
|
revokePreviewTokens: defineFn({
|
|
@@ -1311,6 +1487,64 @@ export const cmsFunctions = {
|
|
|
1311
1487
|
return { ok: true, id: args.id };
|
|
1312
1488
|
},
|
|
1313
1489
|
}),
|
|
1490
|
+
listEntryTerms: defineFn({
|
|
1491
|
+
input: { entryId: 'id' },
|
|
1492
|
+
output: { id: 'id', termId: 'id', taxonomy: 'text', slug: 'text', name: 'text' },
|
|
1493
|
+
effects: [
|
|
1494
|
+
'read:website.Entry',
|
|
1495
|
+
'read:website.SiteMember',
|
|
1496
|
+
'read:website.EntryTerm',
|
|
1497
|
+
'read:website.TaxonomyTerm',
|
|
1498
|
+
],
|
|
1499
|
+
agent: true,
|
|
1500
|
+
handler: async (ctx, args) => {
|
|
1501
|
+
const entry = await entryById(ctx, args.entryId);
|
|
1502
|
+
if (!entry || !(await canAccessSite(ctx, entry.siteId)))
|
|
1503
|
+
return [];
|
|
1504
|
+
const assignments = await ctx.db.select('website.EntryTerm', { entryId: args.entryId });
|
|
1505
|
+
if (!assignments.length)
|
|
1506
|
+
return [];
|
|
1507
|
+
const Term = ctx.table('website.TaxonomyTerm');
|
|
1508
|
+
const terms = await ctx.db.all(from(Term)
|
|
1509
|
+
.where(inArray(Term.id, assignments.map((row) => row.termId)))
|
|
1510
|
+
.orderBy(asc(Term.taxonomy), asc(Term.name)));
|
|
1511
|
+
const byId = new Map(terms.map((term) => [term.id, term]));
|
|
1512
|
+
// The assignment's own id, because that is what unassignTerm is given
|
|
1513
|
+
// and what a remove button has to carry.
|
|
1514
|
+
return assignments.flatMap((assignment) => {
|
|
1515
|
+
const term = byId.get(assignment.termId);
|
|
1516
|
+
return term
|
|
1517
|
+
? [
|
|
1518
|
+
{
|
|
1519
|
+
id: assignment.id,
|
|
1520
|
+
termId: term.id,
|
|
1521
|
+
taxonomy: term.taxonomy,
|
|
1522
|
+
slug: term.slug,
|
|
1523
|
+
name: term.name,
|
|
1524
|
+
},
|
|
1525
|
+
]
|
|
1526
|
+
: [];
|
|
1527
|
+
});
|
|
1528
|
+
},
|
|
1529
|
+
}),
|
|
1530
|
+
unassignTerm: defineFn({
|
|
1531
|
+
input: { entryId: 'id', termId: 'id' },
|
|
1532
|
+
output: { ok: 'bool', id: 'id?', errors: 'json?' },
|
|
1533
|
+
effects: ['read:website.Entry', 'read:website.SiteMember', 'write:website.EntryTerm'],
|
|
1534
|
+
idempotent: true,
|
|
1535
|
+
handler: async (ctx, args) => {
|
|
1536
|
+
const entry = await entryById(ctx, args.entryId);
|
|
1537
|
+
if (!entry || !(await canEditEntry(ctx, entry)))
|
|
1538
|
+
return forbidden();
|
|
1539
|
+
const EntryTerm = ctx.table('website.EntryTerm');
|
|
1540
|
+
// Removing a term that is not there is the state the caller asked for,
|
|
1541
|
+
// so this answers ok either way rather than making a retry an error.
|
|
1542
|
+
await ctx.db.del(deleteFrom(EntryTerm)
|
|
1543
|
+
.where(eq(EntryTerm.entryId, args.entryId))
|
|
1544
|
+
.where(eq(EntryTerm.termId, args.termId)));
|
|
1545
|
+
return { ok: true, id: args.entryId };
|
|
1546
|
+
},
|
|
1547
|
+
}),
|
|
1314
1548
|
saveMediaMetadata: defineFn({
|
|
1315
1549
|
input: {
|
|
1316
1550
|
id: 'id',
|
|
@@ -1371,10 +1605,41 @@ export const cmsFunctions = {
|
|
|
1371
1605
|
return media && (await canAccessSite(ctx, media.siteId)) ? media : null;
|
|
1372
1606
|
},
|
|
1373
1607
|
}),
|
|
1608
|
+
/**
|
|
1609
|
+
* Which pages draw this media item.
|
|
1610
|
+
*
|
|
1611
|
+
* The other half of the guard below: an editor about to delete an image
|
|
1612
|
+
* should be able to see what would break, and `deleteTerm` has shown the
|
|
1613
|
+
* same for taxonomy since it was written.
|
|
1614
|
+
*/
|
|
1615
|
+
mediaUsage: defineFn({
|
|
1616
|
+
input: { id: 'id' },
|
|
1617
|
+
output: { used: 'bool', capped: 'bool', uses: 'json' },
|
|
1618
|
+
effects: [
|
|
1619
|
+
'read:website.MediaMetadata',
|
|
1620
|
+
'read:website.SiteMember',
|
|
1621
|
+
'read:website.Entry',
|
|
1622
|
+
'read:website.EntryRevision',
|
|
1623
|
+
],
|
|
1624
|
+
agent: true,
|
|
1625
|
+
handler: async (ctx, args) => {
|
|
1626
|
+
const media = (await ctx.db.select('website.MediaMetadata', { id: args.id }))[0];
|
|
1627
|
+
if (!media || !(await canAccessSite(ctx, media.siteId)))
|
|
1628
|
+
return { used: false, capped: false, uses: [] };
|
|
1629
|
+
const { uses, capped } = await usageOf(ctx, media);
|
|
1630
|
+
return { used: uses.length > 0, capped, uses };
|
|
1631
|
+
},
|
|
1632
|
+
}),
|
|
1374
1633
|
deleteMediaMetadata: defineFn({
|
|
1375
1634
|
input: { id: 'id' },
|
|
1376
1635
|
output: { ok: 'bool', id: 'id?', errors: 'json?' },
|
|
1377
|
-
effects: [
|
|
1636
|
+
effects: [
|
|
1637
|
+
'read:website.MediaMetadata',
|
|
1638
|
+
'read:website.SiteMember',
|
|
1639
|
+
'read:website.Entry',
|
|
1640
|
+
'read:website.EntryRevision',
|
|
1641
|
+
'write:website.MediaMetadata',
|
|
1642
|
+
],
|
|
1378
1643
|
idempotent: true,
|
|
1379
1644
|
handler: async (ctx, args) => {
|
|
1380
1645
|
const media = (await ctx.db.select('website.MediaMetadata', { id: args.id }))[0];
|
|
@@ -1382,6 +1647,15 @@ export const cmsFunctions = {
|
|
|
1382
1647
|
return { ok: true, id: args.id };
|
|
1383
1648
|
if (!(await canManageStructure(ctx, media.siteId)))
|
|
1384
1649
|
return forbidden();
|
|
1650
|
+
// A page placing this would go on naming an id that no longer resolves,
|
|
1651
|
+
// and nothing anywhere would say why the image stopped appearing.
|
|
1652
|
+
const { uses, capped } = await usageOf(ctx, media);
|
|
1653
|
+
if (uses.length)
|
|
1654
|
+
return invalid('id', 'website.error.mediaInUse');
|
|
1655
|
+
// A scan that did not reach the whole site cannot answer "nothing uses
|
|
1656
|
+
// this" - the same reason a capped preflight is never ok.
|
|
1657
|
+
if (capped)
|
|
1658
|
+
return invalid('id', 'website.error.mediaUsageUnknown');
|
|
1385
1659
|
const Media = ctx.table('website.MediaMetadata');
|
|
1386
1660
|
await ctx.db.del(deleteFrom(Media).where(eq(Media.id, args.id)));
|
|
1387
1661
|
return { ok: true, id: args.id };
|
|
@@ -1434,6 +1708,9 @@ export const cmsFunctions = {
|
|
|
1434
1708
|
if (existing && existing.siteId !== args.siteId)
|
|
1435
1709
|
return invalid('id', 'website.error.immutableOwnership');
|
|
1436
1710
|
const redirects = await ctx.db.select('website.Redirect', { siteId: args.siteId });
|
|
1711
|
+
const taken = redirects.find((redirect) => redirect.id !== args.id && redirect.fromPath === fromPath);
|
|
1712
|
+
if (taken)
|
|
1713
|
+
return invalid('fromPath', 'website.error.duplicateRedirect');
|
|
1437
1714
|
let target = toPath;
|
|
1438
1715
|
for (let depth = 0; depth <= 20; depth += 1) {
|
|
1439
1716
|
if (target === fromPath || depth === 20)
|