@byline/host-tanstack-start 4.15.0 → 4.17.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.
Files changed (183) hide show
  1. package/dist/admin-shell/chrome/admin-menu-drawer.d.ts +2 -1
  2. package/dist/admin-shell/chrome/admin-menu-drawer.js +17 -5
  3. package/dist/admin-shell/chrome/dashboard.js +41 -5
  4. package/dist/admin-shell/chrome/dashboard.test.d.ts +8 -0
  5. package/dist/admin-shell/collections/api.d.ts +2 -2
  6. package/dist/admin-shell/collections/create.d.ts +2 -2
  7. package/dist/admin-shell/collections/create.js +1 -0
  8. package/dist/admin-shell/collections/edit.d.ts +2 -2
  9. package/dist/admin-shell/collections/edit.js +1 -0
  10. package/dist/admin-shell/collections/history.d.ts +4 -12
  11. package/dist/admin-shell/collections/history.js +112 -303
  12. package/dist/admin-shell/collections/reindex-button.d.ts +1 -1
  13. package/dist/admin-shell/collections/reindex-button.js +5 -1
  14. package/dist/admin-shell/collections/restore-version-modal.d.ts +4 -4
  15. package/dist/admin-shell/collections/restore-version-modal.js +8 -24
  16. package/dist/admin-shell/collections/version-history.d.ts +72 -0
  17. package/dist/admin-shell/collections/version-history.js +259 -0
  18. package/dist/admin-shell/singletons/history.d.ts +19 -0
  19. package/dist/admin-shell/singletons/history.js +170 -0
  20. package/dist/admin-shell/singletons/history.test.d.ts +8 -0
  21. package/dist/admin-shell/singletons/preview-link.d.ts +12 -0
  22. package/dist/admin-shell/singletons/preview-link.js +54 -0
  23. package/dist/admin-shell/singletons/view-menu.d.ts +13 -0
  24. package/dist/admin-shell/singletons/view-menu.js +116 -0
  25. package/dist/admin-shell/singletons/view.d.ts +18 -0
  26. package/dist/admin-shell/singletons/view.js +259 -0
  27. package/dist/admin-shell/singletons/view.test.d.ts +8 -0
  28. package/dist/integrations/analytics-agent.d.ts +20 -0
  29. package/dist/integrations/analytics-agent.js +57 -0
  30. package/dist/integrations/analytics-events.d.ts +51 -0
  31. package/dist/integrations/analytics-events.js +129 -0
  32. package/dist/integrations/api-utils.d.ts +18 -4
  33. package/dist/integrations/api-utils.js +10 -3
  34. package/dist/integrations/serve-analytics-agent.d.ts +8 -0
  35. package/dist/integrations/serve-analytics-agent.js +11 -0
  36. package/dist/routes/analytics-range.d.ts +12 -0
  37. package/dist/routes/analytics-range.js +35 -0
  38. package/dist/routes/create-admin-analytics-route.d.ts +8 -0
  39. package/dist/routes/create-admin-analytics-route.js +108 -0
  40. package/dist/routes/create-admin-dashboard-route.js +2 -2
  41. package/dist/routes/create-admin-layout-route.js +10 -4
  42. package/dist/routes/create-collection-api-route.js +3 -3
  43. package/dist/routes/create-collection-create-route.js +4 -3
  44. package/dist/routes/create-collection-edit-route.js +4 -3
  45. package/dist/routes/create-collection-history-route.js +4 -3
  46. package/dist/routes/create-collection-list-route.js +4 -3
  47. package/dist/routes/create-singleton-history-route.d.ts +8 -0
  48. package/dist/routes/create-singleton-history-route.js +99 -0
  49. package/dist/routes/create-singleton-route.d.ts +35 -0
  50. package/dist/routes/create-singleton-route.js +130 -0
  51. package/dist/routes/get-multi-collection-definition.d.ts +10 -0
  52. package/dist/routes/get-multi-collection-definition.js +6 -0
  53. package/dist/routes/get-singleton-definition.d.ts +10 -0
  54. package/dist/routes/get-singleton-definition.js +6 -0
  55. package/dist/routes/index.d.ts +3 -0
  56. package/dist/routes/index.js +3 -0
  57. package/dist/server-fns/actors.d.ts +16 -0
  58. package/dist/server-fns/actors.js +25 -0
  59. package/dist/server-fns/admin-document-presentation.d.ts +30 -0
  60. package/dist/server-fns/admin-document-presentation.js +32 -0
  61. package/dist/server-fns/analytics/index.d.ts +9 -0
  62. package/dist/server-fns/analytics/index.js +2 -0
  63. package/dist/server-fns/analytics/maintenance.d.ts +22 -0
  64. package/dist/server-fns/analytics/maintenance.js +42 -0
  65. package/dist/server-fns/analytics/queries.d.ts +25 -0
  66. package/dist/server-fns/analytics/queries.js +54 -0
  67. package/dist/server-fns/collections/actors.d.ts +1 -10
  68. package/dist/server-fns/collections/actors.js +1 -25
  69. package/dist/server-fns/collections/get.js +9 -26
  70. package/dist/server-fns/collections/reorder.js +1 -1
  71. package/dist/server-fns/collections/scheduled-publication.js +2 -2
  72. package/dist/server-fns/collections/upload.js +3 -3
  73. package/dist/server-fns/collections/utils.d.ts +1 -7
  74. package/dist/server-fns/collections/utils.js +1 -4
  75. package/dist/server-fns/serialise.d.ts +14 -0
  76. package/dist/server-fns/serialise.js +4 -0
  77. package/dist/server-fns/singleton-document-read.d.ts +20 -0
  78. package/dist/server-fns/singleton-document-read.js +10 -0
  79. package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +12 -0
  80. package/dist/server-fns/singletons/cancel-scheduled-publish.js +7 -0
  81. package/dist/server-fns/singletons/change-status.d.ts +14 -0
  82. package/dist/server-fns/singletons/change-status.js +7 -0
  83. package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +14 -0
  84. package/dist/server-fns/singletons/confirm-scheduled-publish.js +9 -0
  85. package/dist/server-fns/singletons/copy-to-locale.d.ts +18 -0
  86. package/dist/server-fns/singletons/copy-to-locale.js +11 -0
  87. package/dist/server-fns/singletons/find-by-version.d.ts +16 -0
  88. package/dist/server-fns/singletons/find-by-version.js +9 -0
  89. package/dist/server-fns/singletons/get-scheduled-publish.d.ts +12 -0
  90. package/dist/server-fns/singletons/get-scheduled-publish.js +7 -0
  91. package/dist/server-fns/singletons/get.d.ts +24 -0
  92. package/dist/server-fns/singletons/get.js +6 -0
  93. package/dist/server-fns/singletons/history.d.ts +31 -0
  94. package/dist/server-fns/singletons/history.js +22 -0
  95. package/dist/server-fns/singletons/index.d.ts +19 -0
  96. package/dist/server-fns/singletons/index.js +12 -0
  97. package/dist/server-fns/singletons/restore-version.d.ts +14 -0
  98. package/dist/server-fns/singletons/restore-version.js +7 -0
  99. package/dist/server-fns/singletons/schedule-publish.d.ts +16 -0
  100. package/dist/server-fns/singletons/schedule-publish.js +10 -0
  101. package/dist/server-fns/singletons/unpublish.d.ts +12 -0
  102. package/dist/server-fns/singletons/unpublish.js +7 -0
  103. package/dist/server-fns/singletons/update.d.ts +14 -0
  104. package/dist/server-fns/singletons/update.js +14 -0
  105. package/dist/vite/analytics-dev.d.ts +22 -0
  106. package/dist/vite/analytics-dev.js +28 -0
  107. package/dist/vite/index.d.ts +1 -0
  108. package/dist/vite/index.js +1 -0
  109. package/dist/vitest-jsdom.test.d.ts +8 -0
  110. package/package.json +28 -16
  111. package/src/admin-shell/chrome/admin-menu-drawer.tsx +19 -5
  112. package/src/admin-shell/chrome/dashboard.test.tsx +199 -0
  113. package/src/admin-shell/chrome/dashboard.tsx +43 -9
  114. package/src/admin-shell/collections/api.tsx +2 -2
  115. package/src/admin-shell/collections/create.tsx +6 -2
  116. package/src/admin-shell/collections/edit.tsx +6 -2
  117. package/src/admin-shell/collections/history.module.css +1 -1
  118. package/src/admin-shell/collections/history.tsx +162 -442
  119. package/src/admin-shell/collections/reindex-button.tsx +4 -2
  120. package/src/admin-shell/collections/restore-version-modal.tsx +19 -25
  121. package/src/admin-shell/collections/version-history.tsx +349 -0
  122. package/src/admin-shell/singletons/history.test.tsx +407 -0
  123. package/src/admin-shell/singletons/history.tsx +185 -0
  124. package/src/admin-shell/singletons/preview-link.tsx +75 -0
  125. package/src/admin-shell/singletons/view-menu.tsx +136 -0
  126. package/src/admin-shell/singletons/view.test.tsx +400 -0
  127. package/src/admin-shell/singletons/view.tsx +351 -0
  128. package/src/integrations/analytics-agent.tsx +91 -0
  129. package/src/integrations/analytics-events.test.node.ts +245 -0
  130. package/src/integrations/analytics-events.ts +224 -0
  131. package/src/integrations/api-utils.ts +34 -9
  132. package/src/integrations/serve-analytics-agent.test.node.ts +20 -0
  133. package/src/integrations/serve-analytics-agent.ts +19 -0
  134. package/src/routes/analytics-range.ts +55 -0
  135. package/src/routes/create-admin-analytics-route.test.node.ts +46 -0
  136. package/src/routes/create-admin-analytics-route.tsx +109 -0
  137. package/src/routes/create-admin-dashboard-route.test.node.ts +70 -0
  138. package/src/routes/create-admin-dashboard-route.tsx +10 -2
  139. package/src/routes/create-admin-layout-route.tsx +15 -8
  140. package/src/routes/create-collection-api-route.tsx +4 -4
  141. package/src/routes/create-collection-create-route.tsx +5 -8
  142. package/src/routes/create-collection-edit-route.tsx +5 -4
  143. package/src/routes/create-collection-history-route.tsx +5 -8
  144. package/src/routes/create-collection-list-route.tsx +5 -8
  145. package/src/routes/create-singleton-history-route.test.node.ts +148 -0
  146. package/src/routes/create-singleton-history-route.tsx +116 -0
  147. package/src/routes/create-singleton-route.test.node.ts +172 -0
  148. package/src/routes/create-singleton-route.tsx +176 -0
  149. package/src/routes/get-multi-collection-definition.ts +16 -0
  150. package/src/routes/get-singleton-definition.ts +16 -0
  151. package/src/routes/index.ts +3 -0
  152. package/src/server-fns/actors.ts +43 -0
  153. package/src/server-fns/admin-document-presentation.ts +78 -0
  154. package/src/server-fns/analytics/index.ts +23 -0
  155. package/src/server-fns/analytics/maintenance.ts +54 -0
  156. package/src/server-fns/analytics/queries.ts +86 -0
  157. package/src/server-fns/collections/actors.ts +1 -52
  158. package/src/server-fns/collections/get.ts +10 -49
  159. package/src/server-fns/collections/reorder.ts +1 -1
  160. package/src/server-fns/collections/scheduled-publication.ts +7 -1
  161. package/src/server-fns/collections/upload-singleton.test.node.ts +164 -0
  162. package/src/server-fns/collections/upload.ts +7 -3
  163. package/src/server-fns/collections/utils.ts +1 -9
  164. package/src/server-fns/serialise.ts +17 -0
  165. package/src/server-fns/singleton-document-read.ts +32 -0
  166. package/src/server-fns/singletons/cancel-scheduled-publish.ts +19 -0
  167. package/src/server-fns/singletons/change-status.ts +19 -0
  168. package/src/server-fns/singletons/confirm-scheduled-publish.ts +23 -0
  169. package/src/server-fns/singletons/copy-to-locale.ts +32 -0
  170. package/src/server-fns/singletons/find-by-version.ts +23 -0
  171. package/src/server-fns/singletons/get-scheduled-publish.ts +19 -0
  172. package/src/server-fns/singletons/get.ts +32 -0
  173. package/src/server-fns/singletons/history.ts +41 -0
  174. package/src/server-fns/singletons/index.ts +20 -0
  175. package/src/server-fns/singletons/restore-version.ts +19 -0
  176. package/src/server-fns/singletons/schedule-publish.ts +24 -0
  177. package/src/server-fns/singletons/singletons.test.node.ts +298 -0
  178. package/src/server-fns/singletons/unpublish.ts +19 -0
  179. package/src/server-fns/singletons/update.ts +31 -0
  180. package/src/vite/analytics-dev.test.node.ts +126 -0
  181. package/src/vite/analytics-dev.ts +56 -0
  182. package/src/vite/index.ts +1 -0
  183. package/src/vitest-jsdom.test.tsx +27 -0
@@ -0,0 +1,130 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { createFileRoute, notFound } from "@tanstack/react-router";
3
+ import { createServerFn } from "@tanstack/react-start";
4
+ import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
5
+ import { buildInitialDataFromFields, documentAbilityKey, getServerConfig, getSingletonAdminConfig } from "@byline/core";
6
+ import { useTranslation } from "@byline/i18n/react";
7
+ import { z } from "zod";
8
+ import { BreadcrumbsClient } from "../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js";
9
+ import { SingletonView } from "../admin-shell/singletons/view.js";
10
+ import { bylineCore } from "../integrations/byline-core.js";
11
+ import { serialise } from "../server-fns/serialise.js";
12
+ import { readSingletonDocument } from "../server-fns/singleton-document-read.js";
13
+ import { getAdminRoutePath } from "./admin-path.js";
14
+ import { getContentLocaleRouteConfig } from "./get-content-locale-route-config.js";
15
+ import { getSingletonDefinition } from "./get-singleton-definition.js";
16
+ const searchSchema = z.object({
17
+ locale: z.string().optional()
18
+ });
19
+ const getSingletonRouteDocument = createServerFn({
20
+ method: 'GET'
21
+ }).validator((input)=>input).handler(async ({ data })=>{
22
+ const definition = getSingletonDefinition(data.singleton);
23
+ if (null == definition) return null;
24
+ const document = await readSingletonDocument({
25
+ singleton: data.singleton,
26
+ locale: data.locale,
27
+ populateRelations: true
28
+ });
29
+ if (null == document) return {
30
+ document: null,
31
+ initialData: serialise(await buildInitialDataFromFields(definition.fields, {
32
+ locale: data.locale ?? getServerConfig().i18n.content.defaultLocale
33
+ }))
34
+ };
35
+ const config = getServerConfig();
36
+ const record = bylineCore().getCollectionRecord(data.singleton);
37
+ let publishedVersion = null;
38
+ if ('published' !== document.status) {
39
+ const published = await config.db.queries.documents.getPublishedVersion({
40
+ collection_id: record.collectionId,
41
+ document_id: String(document.id)
42
+ });
43
+ publishedVersion = published ? serialise({
44
+ id: published.document_id,
45
+ versionId: published.document_version_id,
46
+ status: published.status,
47
+ createdAt: published.created_at,
48
+ updatedAt: published.updated_at
49
+ }) : null;
50
+ }
51
+ const scheduledPublicationEnabled = config.scheduledPublication?.enabled === true;
52
+ let canSchedulePublication = false;
53
+ let scheduledPublish = null;
54
+ if (scheduledPublicationEnabled) {
55
+ const actor = (await getAdminRequestContext()).actor;
56
+ canSchedulePublication = actor?.hasAbility(documentAbilityKey(definition, 'changeStatus')) === true && actor.hasAbility(documentAbilityKey(definition, 'publish'));
57
+ if (canSchedulePublication) scheduledPublish = await getAdminBylineClient().singleton(data.singleton).getScheduledPublish();
58
+ }
59
+ const serializedSchedule = null == scheduledPublish ? null : serialise(scheduledPublish);
60
+ return {
61
+ document: {
62
+ ...document,
63
+ _publishedVersion: publishedVersion,
64
+ _scheduledPublicationEnabled: scheduledPublicationEnabled,
65
+ _canSchedulePublication: canSchedulePublication,
66
+ _scheduledPublish: serializedSchedule,
67
+ ...null == serializedSchedule ? {} : {
68
+ scheduledPublishAt: serializedSchedule.publishAt,
69
+ scheduledPublishVersionId: serializedSchedule.targetVersionId
70
+ }
71
+ },
72
+ initialData: void 0
73
+ };
74
+ });
75
+ function createSingletonRoute(path) {
76
+ const Route = createFileRoute(path)({
77
+ validateSearch: searchSchema,
78
+ loaderDeps: ({ search })=>({
79
+ locale: search.locale
80
+ }),
81
+ loader: async ({ params, deps })=>{
82
+ if (null == getSingletonDefinition(params.singleton)) throw notFound();
83
+ return getSingletonRouteDocument({
84
+ data: {
85
+ singleton: params.singleton,
86
+ locale: deps.locale
87
+ }
88
+ });
89
+ },
90
+ staleTime: 0,
91
+ gcTime: 0,
92
+ shouldReload: true,
93
+ component: function() {
94
+ const routeState = Route.useLoaderData();
95
+ const { singleton } = Route.useParams();
96
+ const { locale } = Route.useSearch();
97
+ const definition = getSingletonDefinition(singleton);
98
+ if (null == definition) throw notFound();
99
+ const { t } = useTranslation('byline-admin');
100
+ const { contentLocales, defaultContentLocale } = getContentLocaleRouteConfig();
101
+ return /*#__PURE__*/ jsxs(Fragment, {
102
+ children: [
103
+ /*#__PURE__*/ jsx(BreadcrumbsClient, {
104
+ breadcrumbs: [
105
+ {
106
+ label: t('chrome.menu.dashboard'),
107
+ href: getAdminRoutePath()
108
+ },
109
+ {
110
+ label: definition.label,
111
+ href: getAdminRoutePath('singletons', singleton)
112
+ }
113
+ ]
114
+ }),
115
+ /*#__PURE__*/ jsx(SingletonView, {
116
+ singletonDefinition: definition,
117
+ adminConfig: getSingletonAdminConfig(singleton) ?? void 0,
118
+ document: routeState.document,
119
+ initialData: routeState.initialData,
120
+ locale: locale,
121
+ contentLocales: contentLocales,
122
+ defaultContentLocale: defaultContentLocale
123
+ })
124
+ ]
125
+ });
126
+ }
127
+ });
128
+ return Route;
129
+ }
130
+ export { createSingletonRoute, getSingletonRouteDocument };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { MultiCollectionDefinition } from '@byline/core';
9
+ /** Resolve only the resource kind supported by the collection route family. */
10
+ export declare function getMultiCollectionDefinition(path: string): MultiCollectionDefinition | null;
@@ -0,0 +1,6 @@
1
+ import { getCollectionDefinition, isSingleton } from "@byline/core";
2
+ function getMultiCollectionDefinition(path) {
3
+ const definition = getCollectionDefinition(path);
4
+ return null == definition || isSingleton(definition) ? null : definition;
5
+ }
6
+ export { getMultiCollectionDefinition };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { SingletonDefinition } from '@byline/core';
9
+ /** Resolve only the resource kind supported by the singleton route family. */
10
+ export declare function getSingletonDefinition(path: string): SingletonDefinition | null;
@@ -0,0 +1,6 @@
1
+ import { getCollectionDefinition, isSingleton } from "@byline/core";
2
+ function getSingletonDefinition(path) {
3
+ const definition = getCollectionDefinition(path);
4
+ return null != definition && isSingleton(definition) ? definition : null;
5
+ }
6
+ export { getSingletonDefinition };
@@ -23,6 +23,7 @@ export { getSignInRoutePath } from './sign-in-path.js';
23
23
  */
24
24
  export { createAdminAccountRoute } from './create-admin-account-route.js';
25
25
  export { createAdminActivityRoute } from './create-admin-activity-route.js';
26
+ export { createAdminAnalyticsRoute } from './create-admin-analytics-route.js';
26
27
  export { createAdminDashboardRoute } from './create-admin-dashboard-route.js';
27
28
  export { createAdminLayoutRoute } from './create-admin-layout-route.js';
28
29
  export { createAdminPermissionsRoute } from './create-admin-permissions-route.js';
@@ -37,3 +38,5 @@ export { createCollectionEditRoute } from './create-collection-edit-route.js';
37
38
  export { createCollectionHistoryRoute } from './create-collection-history-route.js';
38
39
  export { createCollectionListRoute } from './create-collection-list-route.js';
39
40
  export { type CreateSignInRouteOptions, createSignInRoute, } from './create-sign-in-route.js';
41
+ export { createSingletonHistoryRoute } from './create-singleton-history-route.js';
42
+ export { createSingletonRoute } from './create-singleton-route.js';
@@ -2,6 +2,7 @@ export { getAdminRouteId, getAdminRoutePath, isAdminRoutePathActive, isRoutePath
2
2
  export { getSignInRoutePath } from "./sign-in-path.js";
3
3
  export { createAdminAccountRoute } from "./create-admin-account-route.js";
4
4
  export { createAdminActivityRoute } from "./create-admin-activity-route.js";
5
+ export { createAdminAnalyticsRoute } from "./create-admin-analytics-route.js";
5
6
  export { createAdminDashboardRoute } from "./create-admin-dashboard-route.js";
6
7
  export { createAdminLayoutRoute } from "./create-admin-layout-route.js";
7
8
  export { createAdminPermissionsRoute } from "./create-admin-permissions-route.js";
@@ -16,3 +17,5 @@ export { createCollectionEditRoute } from "./create-collection-edit-route.js";
16
17
  export { createCollectionHistoryRoute } from "./create-collection-history-route.js";
17
18
  export { createCollectionListRoute } from "./create-collection-list-route.js";
18
19
  export { createSignInRoute } from "./create-sign-in-route.js";
20
+ export { createSingletonHistoryRoute } from "./create-singleton-history-route.js";
21
+ export { createSingletonRoute } from "./create-singleton-route.js";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export type ActorLabelMap = Record<string, {
9
+ label: string;
10
+ }>;
11
+ /**
12
+ * Batch-resolve admin-user ids to display labels. The admin server-fn layer
13
+ * owns this presentation concern so both collection and singleton history
14
+ * can share it without teaching document storage about admin-realm tables.
15
+ */
16
+ export declare function resolveActorLabels(ids: Iterable<string | null | undefined>): Promise<ActorLabelMap>;
@@ -0,0 +1,25 @@
1
+ import { bylineCore } from "../integrations/byline-core.js";
2
+ function labelFor(row) {
3
+ const name = [
4
+ row.given_name,
5
+ row.family_name
6
+ ].filter(Boolean).join(' ');
7
+ return name || row.username || row.email;
8
+ }
9
+ async function resolveActorLabels(ids) {
10
+ const unique = [
11
+ ...new Set([
12
+ ...ids
13
+ ].filter((id)=>'string' == typeof id && id.length > 0))
14
+ ];
15
+ if (0 === unique.length) return {};
16
+ const store = bylineCore().adminStore;
17
+ if (null == store) return {};
18
+ const rows = await store.adminUsers.getByIds(unique);
19
+ const map = {};
20
+ for (const row of rows)map[row.id] = {
21
+ label: labelFor(row)
22
+ };
23
+ return map;
24
+ }
25
+ export { resolveActorLabels };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { type CollectionDefinition, type PopulateSpec } from '@byline/core';
9
+ export interface AdminDocumentReadInput {
10
+ locale?: string;
11
+ depth?: number;
12
+ populateRelations?: boolean;
13
+ }
14
+ export interface AdminDocumentReadOptions {
15
+ locale: string;
16
+ populate?: PopulateSpec;
17
+ depth?: number;
18
+ status: 'any';
19
+ onMissingLocale: 'empty';
20
+ lenient: true;
21
+ }
22
+ /**
23
+ * Resolve the shared read controls used by collection and singleton editors.
24
+ * Explicit depth is the API-preview mode; otherwise relation-summary mode
25
+ * projects only each target's picker/title fields for first-paint tiles.
26
+ */
27
+ export declare function resolveAdminDocumentRead(definition: CollectionDefinition | null, input: AdminDocumentReadInput): {
28
+ options: AdminDocumentReadOptions;
29
+ populatedTree: boolean;
30
+ };
@@ -0,0 +1,32 @@
1
+ import { buildRelationSummaryPopulateMap, getCollectionAdminConfig, getCollectionDefinition } from "@byline/core";
2
+ function resolveAdminDocumentRead(definition, input) {
3
+ const populateRequested = 'number' == typeof input.depth && input.depth > 0;
4
+ const autoRelationsActive = !populateRequested && true === input.populateRelations;
5
+ let populate;
6
+ let depth;
7
+ if (populateRequested) {
8
+ populate = '*';
9
+ depth = input.depth;
10
+ } else if (autoRelationsActive && null != definition) {
11
+ const populateMap = buildRelationSummaryPopulateMap(definition.fields, (targetPath)=>({
12
+ def: getCollectionDefinition(targetPath),
13
+ admin: getCollectionAdminConfig(targetPath)
14
+ }));
15
+ if (Object.keys(populateMap).length > 0) {
16
+ populate = populateMap;
17
+ depth = 1;
18
+ }
19
+ }
20
+ return {
21
+ options: {
22
+ locale: input.locale ?? 'en',
23
+ populate,
24
+ depth,
25
+ status: 'any',
26
+ onMissingLocale: 'empty',
27
+ lenient: true
28
+ },
29
+ populatedTree: populateRequested || autoRelationsActive
30
+ };
31
+ }
32
+ export { resolveAdminDocumentRead };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export { type DeleteAnalyticsEventsInput, deleteAnalyticsEvents, rebuildAnalyticsDay, } from './maintenance.js';
9
+ export { type AnalyticsRuntimeState, type AnalyticsTopInput, getAnalyticsCountries, getAnalyticsReferrers, getAnalyticsReportCoverage, getAnalyticsRuntime, getAnalyticsSummary, getAnalyticsTop, } from './queries.js';
@@ -0,0 +1,2 @@
1
+ export { deleteAnalyticsEvents, rebuildAnalyticsDay } from "./maintenance.js";
2
+ export { getAnalyticsCountries, getAnalyticsReferrers, getAnalyticsReportCoverage, getAnalyticsRuntime, getAnalyticsSummary, getAnalyticsTop } from "./queries.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export interface DeleteAnalyticsEventsInput {
9
+ from: string;
10
+ to: string;
11
+ visitorHash?: string;
12
+ }
13
+ export declare const deleteAnalyticsEvents: import("@tanstack/react-start").RequiredFetcher<undefined, (input: DeleteAnalyticsEventsInput) => DeleteAnalyticsEventsInput, Promise<{
14
+ deleted: number;
15
+ }>>;
16
+ export declare const rebuildAnalyticsDay: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
17
+ day: string;
18
+ }) => {
19
+ day: string;
20
+ }, Promise<{
21
+ rebuilt: string;
22
+ }>>;
@@ -0,0 +1,42 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { assertAdminActor } from "@byline/admin";
3
+ import { ANALYTICS_ABILITIES } from "@byline/admin/analytics";
4
+ import { getAdminRequestContext } from "@byline/client/server";
5
+ const deleteAnalyticsEvents = createServerFn({
6
+ method: 'POST'
7
+ }).validator((input)=>input).handler(async ({ data })=>{
8
+ await assertAnalyticsMaintenance();
9
+ const { getAnalytics } = await import("@byline/analytics");
10
+ const from = parseInstant(data.from, 'from');
11
+ const to = parseInstant(data.to, 'to');
12
+ const deleted = await getAnalytics().deleteEvents({
13
+ from,
14
+ to,
15
+ ...null == data.visitorHash ? {} : {
16
+ visitorHash: data.visitorHash
17
+ }
18
+ });
19
+ return {
20
+ deleted
21
+ };
22
+ });
23
+ const rebuildAnalyticsDay = createServerFn({
24
+ method: 'POST'
25
+ }).validator((input)=>input).handler(async ({ data })=>{
26
+ await assertAnalyticsMaintenance();
27
+ const { getAnalytics } = await import("@byline/analytics");
28
+ await getAnalytics().rebuildDay(data.day);
29
+ return {
30
+ rebuilt: data.day
31
+ };
32
+ });
33
+ async function assertAnalyticsMaintenance() {
34
+ const context = await getAdminRequestContext();
35
+ assertAdminActor(context, ANALYTICS_ABILITIES.maintain);
36
+ }
37
+ function parseInstant(value, label) {
38
+ const date = new Date(value);
39
+ if (Number.isNaN(date.valueOf())) throw new Error(`analytics ${label} must be an ISO instant`);
40
+ return date;
41
+ }
42
+ export { deleteAnalyticsEvents, rebuildAnalyticsDay };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { AnalyticsCountryTotal, AnalyticsDateRange, AnalyticsEventKind, AnalyticsPathTotal, AnalyticsRankedTotals, AnalyticsReferrerTotal, AnalyticsReportCoverage, AnalyticsSummary } from '@byline/analytics';
9
+ export interface AnalyticsTopInput extends AnalyticsDateRange {
10
+ kind: AnalyticsEventKind;
11
+ limit?: number;
12
+ }
13
+ export interface AnalyticsRuntimeState {
14
+ enabled: boolean;
15
+ }
16
+ export declare const getAnalyticsRuntime: import("@tanstack/react-start").OptionalFetcher<undefined, undefined, Promise<AnalyticsRuntimeState>>;
17
+ export declare const getAnalyticsSummary: import("@tanstack/react-start").RequiredFetcher<undefined, (input: AnalyticsDateRange) => AnalyticsDateRange, Promise<AnalyticsSummary>>;
18
+ export declare const getAnalyticsReportCoverage: import("@tanstack/react-start").OptionalFetcher<undefined, undefined, Promise<AnalyticsReportCoverage>>;
19
+ export declare const getAnalyticsTop: import("@tanstack/react-start").RequiredFetcher<undefined, (input: AnalyticsTopInput) => AnalyticsTopInput, Promise<AnalyticsRankedTotals<AnalyticsPathTotal>>>;
20
+ export declare const getAnalyticsReferrers: import("@tanstack/react-start").RequiredFetcher<undefined, (input: AnalyticsDateRange & {
21
+ limit?: number;
22
+ }) => AnalyticsDateRange & {
23
+ limit?: number;
24
+ }, Promise<AnalyticsRankedTotals<AnalyticsReferrerTotal>>>;
25
+ export declare const getAnalyticsCountries: import("@tanstack/react-start").RequiredFetcher<undefined, (input: AnalyticsDateRange) => AnalyticsDateRange, Promise<AnalyticsCountryTotal[]>>;
@@ -0,0 +1,54 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { assertAdminActor, requireAdminActor } from "@byline/admin";
3
+ import { ANALYTICS_ABILITIES } from "@byline/admin/analytics";
4
+ import { getAdminRequestContext } from "@byline/client/server";
5
+ const getAnalyticsRuntime = createServerFn({
6
+ method: 'GET'
7
+ }).handler(async ()=>{
8
+ const context = await getAdminRequestContext();
9
+ requireAdminActor(context, 'reading analytics runtime state');
10
+ const { isAnalyticsRegistered } = await import("@byline/analytics");
11
+ return {
12
+ enabled: isAnalyticsRegistered()
13
+ };
14
+ });
15
+ const getAnalyticsSummary = createServerFn({
16
+ method: 'GET'
17
+ }).validator((input)=>input).handler(async ({ data })=>{
18
+ await assertAnalyticsRead();
19
+ const { getAnalytics } = await import("@byline/analytics");
20
+ return getAnalytics().getSummary(data);
21
+ });
22
+ const getAnalyticsReportCoverage = createServerFn({
23
+ method: 'GET'
24
+ }).handler(async ()=>{
25
+ await assertAnalyticsRead();
26
+ const { getAnalytics } = await import("@byline/analytics");
27
+ return getAnalytics().getReportCoverage();
28
+ });
29
+ const getAnalyticsTop = createServerFn({
30
+ method: 'GET'
31
+ }).validator((input)=>input).handler(async ({ data })=>{
32
+ await assertAnalyticsRead();
33
+ const { getAnalytics } = await import("@byline/analytics");
34
+ return getAnalytics().getTopPaths(data);
35
+ });
36
+ const getAnalyticsReferrers = createServerFn({
37
+ method: 'GET'
38
+ }).validator((input)=>input).handler(async ({ data })=>{
39
+ await assertAnalyticsRead();
40
+ const { getAnalytics } = await import("@byline/analytics");
41
+ return getAnalytics().getReferrers(data);
42
+ });
43
+ const getAnalyticsCountries = createServerFn({
44
+ method: 'GET'
45
+ }).validator((input)=>input).handler(async ({ data })=>{
46
+ await assertAnalyticsRead();
47
+ const { getAnalytics } = await import("@byline/analytics");
48
+ return getAnalytics().getCountries(data);
49
+ });
50
+ async function assertAnalyticsRead() {
51
+ const context = await getAdminRequestContext();
52
+ assertAdminActor(context, ANALYTICS_ABILITIES.read);
53
+ }
54
+ export { getAnalyticsCountries, getAnalyticsReferrers, getAnalyticsReportCoverage, getAnalyticsRuntime, getAnalyticsSummary, getAnalyticsTop };
@@ -5,13 +5,4 @@
5
5
  *
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
- export type ActorLabelMap = Record<string, {
9
- label: string;
10
- }>;
11
- /**
12
- * Batch-resolve admin-user ids to display labels. Accepts the raw
13
- * `createdBy` values straight off a page of shaped documents — nullish
14
- * and duplicate entries are tolerated and deduplicated. Returns `{}`
15
- * when there is nothing to resolve or no admin store is configured.
16
- */
17
- export declare function resolveActorLabels(ids: Iterable<string | null | undefined>): Promise<ActorLabelMap>;
8
+ export { type ActorLabelMap, resolveActorLabels } from '../actors.js';
@@ -1,25 +1 @@
1
- import { bylineCore } from "../../integrations/byline-core.js";
2
- function labelFor(row) {
3
- const name = [
4
- row.given_name,
5
- row.family_name
6
- ].filter(Boolean).join(' ');
7
- return name || row.username || row.email;
8
- }
9
- async function resolveActorLabels(ids) {
10
- const unique = [
11
- ...new Set([
12
- ...ids
13
- ].filter((id)=>'string' == typeof id && id.length > 0))
14
- ];
15
- if (0 === unique.length) return {};
16
- const store = bylineCore().adminStore;
17
- if (null == store) return {};
18
- const rows = await store.adminUsers.getByIds(unique);
19
- const map = {};
20
- for (const row of rows)map[row.id] = {
21
- label: labelFor(row)
22
- };
23
- return map;
24
- }
25
- export { resolveActorLabels };
1
+ export { resolveActorLabels } from "../actors.js";
@@ -1,7 +1,8 @@
1
1
  import { createServerFn } from "@tanstack/react-start";
2
2
  import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
3
- import { BylineError, ERR_NOT_FOUND, ErrorCodes, buildRelationSummaryPopulateMap, collectionAbilityKey, getCollectionAdminConfig, getCollectionDefinition, getCollectionSchemasForPath, getLogger, getServerConfig } from "@byline/core";
3
+ import { BylineError, ERR_NOT_FOUND, ErrorCodes, documentAbilityKey, getCollectionSchemasForPath, getLogger, getServerConfig } from "@byline/core";
4
4
  import { ensureCollection } from "../../integrations/api-utils.js";
5
+ import { resolveAdminDocumentRead } from "../admin-document-presentation.js";
5
6
  import { serialise } from "./utils.js";
6
7
  const getDocumentFn = createServerFn({
7
8
  method: 'GET'
@@ -17,30 +18,13 @@ const getDocumentFn = createServerFn({
17
18
  }).log(logger);
18
19
  const client = getAdminBylineClient();
19
20
  const handle = client.collection(path);
20
- const populateRequested = 'number' == typeof depth && depth > 0;
21
- const autoRelationsActive = !populateRequested && true === populateRelations;
22
- let populate;
23
- let resolvedDepth;
24
- if (populateRequested) {
25
- populate = '*';
26
- resolvedDepth = depth;
27
- } else if (autoRelationsActive) {
28
- const populateMap = buildRelationSummaryPopulateMap(config.definition.fields, (targetPath)=>({
29
- def: getCollectionDefinition(targetPath),
30
- admin: getCollectionAdminConfig(targetPath)
31
- }));
32
- if (Object.keys(populateMap).length > 0) {
33
- populate = populateMap;
34
- resolvedDepth = 1;
35
- }
36
- }
21
+ const { options: readOptions, populatedTree } = resolveAdminDocumentRead(config.definition, {
22
+ locale,
23
+ depth,
24
+ populateRelations
25
+ });
37
26
  const document = await handle.findById(id, {
38
- locale: locale ?? 'en',
39
- populate,
40
- depth: resolvedDepth,
41
- status: 'any',
42
- onMissingLocale: 'empty',
43
- lenient: true
27
+ ...readOptions
44
28
  });
45
29
  if (!document) throw ERR_NOT_FOUND({
46
30
  message: 'Document not found',
@@ -50,7 +34,6 @@ const getDocumentFn = createServerFn({
50
34
  }
51
35
  }).log(logger);
52
36
  const serialised = serialise(document);
53
- const populatedTree = populateRequested || autoRelationsActive;
54
37
  const parsed = 'all' === locale || populatedTree ? serialised : (()=>{
55
38
  const { get } = getCollectionSchemasForPath(path);
56
39
  return get.parse(serialised);
@@ -77,7 +60,7 @@ const getDocumentFn = createServerFn({
77
60
  let scheduledPublish = null;
78
61
  if (scheduledPublicationEnabled) {
79
62
  const actor = (await getAdminRequestContext()).actor;
80
- canSchedulePublication = actor?.hasAbility(collectionAbilityKey(path, 'changeStatus')) === true && actor.hasAbility(collectionAbilityKey(path, 'publish'));
63
+ canSchedulePublication = actor?.hasAbility(documentAbilityKey(config.definition, 'changeStatus')) === true && actor.hasAbility(documentAbilityKey(config.definition, 'publish'));
81
64
  if (canSchedulePublication) scheduledPublish = await handle.getScheduledPublish(id);
82
65
  }
83
66
  const serializedSchedule = null == scheduledPublish ? null : serialise(scheduledPublish);
@@ -21,7 +21,7 @@ const reorderCollectionDocument = createServerFn({
21
21
  }
22
22
  }).log(logger);
23
23
  const requestContext = await getAdminRequestContext();
24
- assertActorCanPerform(requestContext, path, 'update');
24
+ assertActorCanPerform(requestContext, config.definition, 'update');
25
25
  const serverConfig = getServerConfig();
26
26
  const collectionId = config.collection.id;
27
27
  const canonical = await serverConfig.db.queries.documents.getCanonicalDocumentOrder({
@@ -1,6 +1,6 @@
1
1
  import { createServerFn } from "@tanstack/react-start";
2
2
  import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
3
- import { ERR_NOT_FOUND, ERR_VALIDATION } from "@byline/core";
3
+ import { ERR_NOT_FOUND, ERR_VALIDATION, isSingleton } from "@byline/core";
4
4
  import { listDocumentPublishSchedules } from "@byline/core/services";
5
5
  import { bylineCore } from "../../integrations/byline-core.js";
6
6
  import { omitScheduleExecutionState } from "./scheduled-publication-response.js";
@@ -138,7 +138,7 @@ const listScheduledPublications = createServerFn({
138
138
  return {
139
139
  ...serializeSchedule(schedule),
140
140
  collectionPath: definition?.path ?? schedule.collectionId,
141
- collectionLabel: definition?.labels.plural ?? definition?.path ?? schedule.collectionId,
141
+ collectionLabel: null == definition ? schedule.collectionId : isSingleton(definition) ? definition.label : definition.labels.plural,
142
142
  documentPath,
143
143
  lastAuthorizedByName: null == schedule.lastAuthorizedBy ? null : authorizerNames.get(schedule.lastAuthorizedBy) ?? null
144
144
  };
@@ -4,7 +4,7 @@ import { ERR_NOT_FOUND, ERR_VALIDATION, getServerConfig, getUploadFields } from
4
4
  import { extractImageMeta, generateImageVariants, isBypassMimeType } from "@byline/core/image";
5
5
  import { getLogger, withLogContext } from "@byline/core/logger";
6
6
  import { uploadField } from "@byline/core/services";
7
- import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { ensureDocumentResource } from "../../integrations/api-utils.js";
8
8
  function parseUploadFormData(data) {
9
9
  if (!(data instanceof FormData)) throw new Error('Expected FormData upload payload.');
10
10
  const collectionEntry = data.get('collection');
@@ -64,9 +64,9 @@ const uploadCollectionField = createServerFn({
64
64
  module: 'upload',
65
65
  function: 'uploadCollectionField'
66
66
  }, async ()=>{
67
- const config = await ensureCollection(collectionPath);
67
+ const config = await ensureDocumentResource(collectionPath);
68
68
  if (null == config) throw ERR_NOT_FOUND({
69
- message: 'Collection not found.',
69
+ message: 'Document resource not found.',
70
70
  details: {
71
71
  collectionPath
72
72
  }
@@ -5,10 +5,4 @@
5
5
  *
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
- /**
9
- * Deep-serialise a value through JSON to convert Date objects to ISO strings.
10
- * TanStack Start server functions pass return values directly during SSR
11
- * (no automatic JSON round-trip), so Date instances from the DB driver would
12
- * fail downstream Zod z.string() checks without this explicit step.
13
- */
14
- export declare function serialise<T>(value: T): T;
8
+ export { serialise } from '../serialise.js';
@@ -1,4 +1 @@
1
- function serialise(value) {
2
- return JSON.parse(JSON.stringify(value));
3
- }
4
- export { serialise };
1
+ export { serialise } from "../serialise.js";