@headless-lms/core 0.4.0 → 0.6.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 (249) hide show
  1. package/dist/assets/events.d.ts +15 -9
  2. package/dist/assets/file-urls.d.ts +14 -7
  3. package/dist/assets/file-urls.js +15 -4
  4. package/dist/assets/file-urls.js.map +1 -1
  5. package/dist/assets/index.d.ts +4 -4
  6. package/dist/assets/index.js +3 -3
  7. package/dist/assets/ports.d.ts +34 -15
  8. package/dist/assets/service.d.ts +30 -14
  9. package/dist/assets/service.js +233 -54
  10. package/dist/assets/service.js.map +1 -1
  11. package/dist/automations/actions.js.map +1 -1
  12. package/dist/automations/catalog.js +68 -3
  13. package/dist/automations/catalog.js.map +1 -1
  14. package/dist/automations/community-derivations.js +16 -16
  15. package/dist/automations/community-derivations.js.map +1 -1
  16. package/dist/automations/community-mail.d.ts +34 -4
  17. package/dist/automations/community-mail.js +32 -4
  18. package/dist/automations/community-mail.js.map +1 -1
  19. package/dist/automations/event-permissions.d.ts +12 -0
  20. package/dist/automations/event-permissions.js +38 -0
  21. package/dist/automations/event-permissions.js.map +1 -0
  22. package/dist/automations/index.d.ts +2 -1
  23. package/dist/automations/index.js +2 -1
  24. package/dist/automations/notifications/applicable.d.ts +8 -0
  25. package/dist/automations/notifications/applicable.js +12 -0
  26. package/dist/automations/notifications/applicable.js.map +1 -0
  27. package/dist/automations/notifications/debounce.js +1 -1
  28. package/dist/automations/notifications/debounce.js.map +1 -1
  29. package/dist/automations/notifications/defaults.js +33 -3
  30. package/dist/automations/notifications/defaults.js.map +1 -1
  31. package/dist/automations/notifications/processor.js +1 -1
  32. package/dist/automations/notifications/processor.js.map +1 -1
  33. package/dist/automations/notifications/rules/content.js +60 -3
  34. package/dist/automations/notifications/rules/content.js.map +1 -1
  35. package/dist/automations/notifications/rules/discussion.js +29 -29
  36. package/dist/automations/notifications/rules/discussion.js.map +1 -1
  37. package/dist/automations/notifications/rules/entitlements.js +36 -9
  38. package/dist/automations/notifications/rules/entitlements.js.map +1 -1
  39. package/dist/automations/notifications/rules/events.d.ts +9 -0
  40. package/dist/automations/notifications/rules/events.js +27 -0
  41. package/dist/automations/notifications/rules/events.js.map +1 -0
  42. package/dist/automations/notifications/rules/organizations.js +17 -10
  43. package/dist/automations/notifications/rules/organizations.js.map +1 -1
  44. package/dist/automations/notifications/rules/recipients.js +0 -1
  45. package/dist/automations/notifications/rules/recipients.js.map +1 -1
  46. package/dist/automations/notifications/service.d.ts +8 -6
  47. package/dist/automations/notifications/service.js +12 -12
  48. package/dist/automations/notifications/service.js.map +1 -1
  49. package/dist/automations/notifications/types.js +94 -7
  50. package/dist/automations/notifications/types.js.map +1 -1
  51. package/dist/automations/ports.d.ts +30 -31
  52. package/dist/automations/service.d.ts +20 -17
  53. package/dist/automations/service.js +90 -61
  54. package/dist/automations/service.js.map +1 -1
  55. package/dist/content/event-sight.d.ts +11 -0
  56. package/dist/content/event-sight.js +11 -0
  57. package/dist/content/event-sight.js.map +1 -0
  58. package/dist/content/events.d.ts +465 -88
  59. package/dist/content/events.js +34 -1
  60. package/dist/content/events.js.map +1 -1
  61. package/dist/content/index.d.ts +6 -4
  62. package/dist/content/index.js +3 -1
  63. package/dist/content/jobs.d.ts +16 -0
  64. package/dist/content/jobs.js +37 -0
  65. package/dist/content/jobs.js.map +1 -0
  66. package/dist/content/model.d.ts +2 -2
  67. package/dist/content/notification-categories.d.ts +5 -4
  68. package/dist/content/notification-categories.js +15 -6
  69. package/dist/content/notification-categories.js.map +1 -1
  70. package/dist/content/ports.d.ts +113 -68
  71. package/dist/content/service.d.ts +103 -67
  72. package/dist/content/service.js +739 -152
  73. package/dist/content/service.js.map +1 -1
  74. package/dist/content/titles.d.ts +6 -3
  75. package/dist/content/titles.js +37 -24
  76. package/dist/content/titles.js.map +1 -1
  77. package/dist/content/types.d.ts +2 -2
  78. package/dist/discussion/authors.d.ts +4 -2
  79. package/dist/discussion/authors.js +9 -5
  80. package/dist/discussion/authors.js.map +1 -1
  81. package/dist/discussion/chats.d.ts +56 -37
  82. package/dist/discussion/chats.js +392 -160
  83. package/dist/discussion/chats.js.map +1 -1
  84. package/dist/discussion/events.d.ts +85 -47
  85. package/dist/discussion/events.js +41 -3
  86. package/dist/discussion/events.js.map +1 -1
  87. package/dist/discussion/index.d.ts +8 -5
  88. package/dist/discussion/index.js +5 -3
  89. package/dist/discussion/live.d.ts +106 -0
  90. package/dist/discussion/live.js +642 -0
  91. package/dist/discussion/live.js.map +1 -0
  92. package/dist/discussion/mentions.js +2 -2
  93. package/dist/discussion/mentions.js.map +1 -1
  94. package/dist/discussion/ports.d.ts +182 -100
  95. package/dist/discussion/ports.js.map +1 -1
  96. package/dist/discussion/posts.d.ts +45 -39
  97. package/dist/discussion/posts.js +266 -152
  98. package/dist/discussion/posts.js.map +1 -1
  99. package/dist/discussion/rules.d.ts +18 -0
  100. package/dist/discussion/rules.js +22 -0
  101. package/dist/discussion/rules.js.map +1 -0
  102. package/dist/discussion/service.d.ts +37 -31
  103. package/dist/discussion/service.js +250 -186
  104. package/dist/discussion/service.js.map +1 -1
  105. package/dist/discussion/subscriptions.d.ts +8 -8
  106. package/dist/discussion/subscriptions.js +28 -12
  107. package/dist/discussion/subscriptions.js.map +1 -1
  108. package/dist/discussion/titles.d.ts +4 -2
  109. package/dist/discussion/titles.js +27 -13
  110. package/dist/discussion/titles.js.map +1 -1
  111. package/dist/entitlements/events.d.ts +58 -58
  112. package/dist/entitlements/index.d.ts +4 -4
  113. package/dist/entitlements/index.js +2 -2
  114. package/dist/entitlements/model.d.ts +2 -2
  115. package/dist/entitlements/ports.d.ts +65 -39
  116. package/dist/entitlements/rules.d.ts +3 -6
  117. package/dist/entitlements/rules.js +5 -10
  118. package/dist/entitlements/rules.js.map +1 -1
  119. package/dist/entitlements/service.d.ts +40 -28
  120. package/dist/entitlements/service.js +369 -162
  121. package/dist/entitlements/service.js.map +1 -1
  122. package/dist/identity/devices.d.ts +3 -3
  123. package/dist/identity/devices.js +6 -4
  124. package/dist/identity/devices.js.map +1 -1
  125. package/dist/identity/index.d.ts +2 -2
  126. package/dist/identity/index.js +2 -2
  127. package/dist/identity/notification-settings.d.ts +1 -1
  128. package/dist/identity/notification-settings.js +2 -1
  129. package/dist/identity/notification-settings.js.map +1 -1
  130. package/dist/identity/ports.d.ts +7 -7
  131. package/dist/identity/service.d.ts +8 -7
  132. package/dist/identity/service.js +16 -12
  133. package/dist/identity/service.js.map +1 -1
  134. package/dist/integrations/events.d.ts +3 -3
  135. package/dist/integrations/ports.d.ts +9 -8
  136. package/dist/integrations/service.d.ts +9 -8
  137. package/dist/integrations/service.js +31 -14
  138. package/dist/integrations/service.js.map +1 -1
  139. package/dist/organizations/default-roles.d.ts +22 -0
  140. package/dist/organizations/default-roles.js +89 -0
  141. package/dist/organizations/default-roles.js.map +1 -0
  142. package/dist/organizations/effective-permissions.d.ts +47 -0
  143. package/dist/organizations/effective-permissions.js +118 -0
  144. package/dist/organizations/effective-permissions.js.map +1 -0
  145. package/dist/organizations/events.d.ts +326 -181
  146. package/dist/organizations/events.js +90 -18
  147. package/dist/organizations/events.js.map +1 -1
  148. package/dist/organizations/index.d.ts +14 -10
  149. package/dist/organizations/index.js +7 -3
  150. package/dist/organizations/members.d.ts +66 -8
  151. package/dist/organizations/members.js.map +1 -1
  152. package/dist/organizations/model.d.ts +2 -2
  153. package/dist/organizations/notification-catalog.d.ts +31 -0
  154. package/dist/organizations/notification-catalog.js +32 -0
  155. package/dist/organizations/notification-catalog.js.map +1 -0
  156. package/dist/organizations/notification-categories.d.ts +6 -4
  157. package/dist/organizations/notification-categories.js +25 -10
  158. package/dist/organizations/notification-categories.js.map +1 -1
  159. package/dist/organizations/org-roles.d.ts +33 -0
  160. package/dist/organizations/org-roles.js +49 -0
  161. package/dist/organizations/org-roles.js.map +1 -0
  162. package/dist/organizations/permissions.d.ts +311 -0
  163. package/dist/organizations/permissions.js +327 -0
  164. package/dist/organizations/permissions.js.map +1 -0
  165. package/dist/organizations/ports.d.ts +187 -65
  166. package/dist/organizations/service.d.ts +102 -35
  167. package/dist/organizations/service.js +1254 -327
  168. package/dist/organizations/service.js.map +1 -1
  169. package/dist/organizations/testing.d.ts +18 -0
  170. package/dist/organizations/testing.js +37 -0
  171. package/dist/organizations/testing.js.map +1 -0
  172. package/dist/organizations/types.d.ts +5 -16
  173. package/dist/progress/events.d.ts +14 -14
  174. package/dist/progress/index.d.ts +2 -2
  175. package/dist/progress/ports.d.ts +11 -3
  176. package/dist/progress/service.d.ts +6 -5
  177. package/dist/progress/service.js +21 -6
  178. package/dist/progress/service.js.map +1 -1
  179. package/dist/reporting/admin/index.d.ts +6 -4
  180. package/dist/reporting/admin/model.d.ts +36 -3
  181. package/dist/reporting/admin/ports.d.ts +39 -14
  182. package/dist/reporting/admin/service.d.ts +32 -11
  183. package/dist/reporting/admin/service.js +102 -18
  184. package/dist/reporting/admin/service.js.map +1 -1
  185. package/dist/reporting/events/index.d.ts +27 -0
  186. package/dist/reporting/events/index.js +20 -0
  187. package/dist/reporting/events/index.js.map +1 -0
  188. package/dist/reporting/member/calendar.d.ts +11 -0
  189. package/dist/reporting/member/calendar.js +75 -0
  190. package/dist/reporting/member/calendar.js.map +1 -0
  191. package/dist/reporting/member/index.d.ts +3 -2
  192. package/dist/reporting/member/model.d.ts +29 -5
  193. package/dist/reporting/member/ports.d.ts +49 -29
  194. package/dist/reporting/member/service.d.ts +57 -29
  195. package/dist/reporting/member/service.js +345 -114
  196. package/dist/reporting/member/service.js.map +1 -1
  197. package/dist/shared/actor.d.ts +23 -3
  198. package/dist/shared/errors.d.ts +2 -1
  199. package/dist/shared/errors.js +3 -1
  200. package/dist/shared/errors.js.map +1 -1
  201. package/dist/shared/id.d.ts +4 -0
  202. package/dist/shared/id.js +4 -0
  203. package/dist/shared/id.js.map +1 -1
  204. package/dist/shared/logger.d.ts +2 -1
  205. package/dist/shared/logger.js +10 -1
  206. package/dist/shared/logger.js.map +1 -1
  207. package/dist/shared/ports.d.ts +25 -5
  208. package/dist/types/content.d.ts +2 -2
  209. package/dist/types/discussion.d.ts +4 -5
  210. package/dist/types/email-templates.d.ts +22 -3
  211. package/dist/types/entitlements.d.ts +2 -2
  212. package/dist/types/identity.d.ts +13 -16
  213. package/dist/types/index.d.ts +9 -9
  214. package/dist/types/index.js +2 -1
  215. package/dist/types/notifications.d.ts +4 -4
  216. package/dist/types/organizations.d.ts +3 -2
  217. package/dist/types/ports.d.ts +8 -1
  218. package/dist/types/ports.js +7 -1
  219. package/dist/types/ports.js.map +1 -0
  220. package/dist/types/schemas/assets.d.ts +5 -5
  221. package/dist/types/schemas/assets.js +4 -3
  222. package/dist/types/schemas/assets.js.map +1 -1
  223. package/dist/types/schemas/content.d.ts +599 -13
  224. package/dist/types/schemas/content.js +219 -8
  225. package/dist/types/schemas/content.js.map +1 -1
  226. package/dist/types/schemas/discussion.d.ts +280 -57
  227. package/dist/types/schemas/discussion.js +77 -3
  228. package/dist/types/schemas/discussion.js.map +1 -1
  229. package/dist/types/schemas/entitlements.d.ts +21 -8
  230. package/dist/types/schemas/entitlements.js +11 -4
  231. package/dist/types/schemas/entitlements.js.map +1 -1
  232. package/dist/types/schemas/index.d.ts +5 -5
  233. package/dist/types/schemas/index.js +5 -5
  234. package/dist/types/schemas/notifications.d.ts +11 -10
  235. package/dist/types/schemas/notifications.js +3 -2
  236. package/dist/types/schemas/notifications.js.map +1 -1
  237. package/dist/types/schemas/organizations.d.ts +99 -60
  238. package/dist/types/schemas/organizations.js +80 -27
  239. package/dist/types/schemas/organizations.js.map +1 -1
  240. package/dist/types/schemas/progress.d.ts +3 -4
  241. package/dist/types/schemas/progress.js +0 -1
  242. package/dist/types/schemas/progress.js.map +1 -1
  243. package/package.json +1 -1
  244. package/dist/organizations/access-scopes.d.ts +0 -19
  245. package/dist/organizations/access-scopes.js +0 -89
  246. package/dist/organizations/access-scopes.js.map +0 -1
  247. package/dist/organizations/roles.d.ts +0 -19
  248. package/dist/organizations/roles.js +0 -82
  249. package/dist/organizations/roles.js.map +0 -1
@@ -13,6 +13,7 @@ declare const assetEvents: {
13
13
  size: number;
14
14
  status: "pending" | "ready";
15
15
  uploadedBy: string;
16
+ contentId: string | null;
16
17
  createdAt: Date;
17
18
  updatedAt: Date;
18
19
  }, "asset.created", 1>, {
@@ -26,13 +27,12 @@ declare const assetEvents: {
26
27
  size: number;
27
28
  status: "pending" | "ready";
28
29
  uploadedBy: string;
30
+ contentId: string | null;
29
31
  createdAt: unknown;
30
32
  updatedAt: unknown;
31
33
  }, import("zod").ZodObject<{
32
- id: import("zod").ZodString;
33
34
  orgId: import("zod").ZodString;
34
- createdAt: import("zod").ZodCoercedDate<unknown>;
35
- updatedAt: import("zod").ZodCoercedDate<unknown>;
35
+ id: import("zod").ZodString;
36
36
  kind: import("zod").ZodEnum<{
37
37
  image: "image";
38
38
  video: "video";
@@ -51,6 +51,8 @@ declare const assetEvents: {
51
51
  filename: import("zod").ZodString;
52
52
  contentType: import("zod").ZodString;
53
53
  size: import("zod").ZodNumber;
54
+ createdAt: import("zod").ZodCoercedDate<unknown>;
55
+ updatedAt: import("zod").ZodCoercedDate<unknown>;
54
56
  }, import("zod/v4/core").$strip>>;
55
57
  assetReady: RuntimeEventDefinition<VersionedDomainEvent<{
56
58
  orgId: string;
@@ -63,6 +65,7 @@ declare const assetEvents: {
63
65
  size: number;
64
66
  status: "pending" | "ready";
65
67
  uploadedBy: string;
68
+ contentId: string | null;
66
69
  createdAt: Date;
67
70
  updatedAt: Date;
68
71
  }, "asset.ready", 1>, {
@@ -76,13 +79,12 @@ declare const assetEvents: {
76
79
  size: number;
77
80
  status: "pending" | "ready";
78
81
  uploadedBy: string;
82
+ contentId: string | null;
79
83
  createdAt: unknown;
80
84
  updatedAt: unknown;
81
85
  }, import("zod").ZodObject<{
82
- id: import("zod").ZodString;
83
86
  orgId: import("zod").ZodString;
84
- createdAt: import("zod").ZodCoercedDate<unknown>;
85
- updatedAt: import("zod").ZodCoercedDate<unknown>;
87
+ id: import("zod").ZodString;
86
88
  kind: import("zod").ZodEnum<{
87
89
  image: "image";
88
90
  video: "video";
@@ -101,6 +103,8 @@ declare const assetEvents: {
101
103
  filename: import("zod").ZodString;
102
104
  contentType: import("zod").ZodString;
103
105
  size: import("zod").ZodNumber;
106
+ createdAt: import("zod").ZodCoercedDate<unknown>;
107
+ updatedAt: import("zod").ZodCoercedDate<unknown>;
104
108
  }, import("zod/v4/core").$strip>>;
105
109
  assetDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
106
110
  orgId: string;
@@ -113,6 +117,7 @@ declare const assetEvents: {
113
117
  size: number;
114
118
  status: "pending" | "ready";
115
119
  uploadedBy: string;
120
+ contentId: string | null;
116
121
  createdAt: Date;
117
122
  updatedAt: Date;
118
123
  }, "asset.deleted", 1>, {
@@ -126,13 +131,12 @@ declare const assetEvents: {
126
131
  size: number;
127
132
  status: "pending" | "ready";
128
133
  uploadedBy: string;
134
+ contentId: string | null;
129
135
  createdAt: unknown;
130
136
  updatedAt: unknown;
131
137
  }, import("zod").ZodObject<{
132
- id: import("zod").ZodString;
133
138
  orgId: import("zod").ZodString;
134
- createdAt: import("zod").ZodCoercedDate<unknown>;
135
- updatedAt: import("zod").ZodCoercedDate<unknown>;
139
+ id: import("zod").ZodString;
136
140
  kind: import("zod").ZodEnum<{
137
141
  image: "image";
138
142
  video: "video";
@@ -151,6 +155,8 @@ declare const assetEvents: {
151
155
  filename: import("zod").ZodString;
152
156
  contentType: import("zod").ZodString;
153
157
  size: import("zod").ZodNumber;
158
+ createdAt: import("zod").ZodCoercedDate<unknown>;
159
+ updatedAt: import("zod").ZodCoercedDate<unknown>;
154
160
  }, import("zod/v4/core").$strip>>;
155
161
  };
156
162
  type AssetCreated = EventOf<typeof assetEvents.assetCreated>;
@@ -1,20 +1,27 @@
1
+ import { SpaceImage } from "../types/schemas/content.js";
2
+ import "../types/index.js";
1
3
  //#region src/assets/file-urls.d.ts
2
- type ThumbnailOwner = 'course' | 'download' | 'community';
3
- /** Stable, access-checked URLs on the API for every file a response refers to.
4
- * Each one is answered by a route that checks access, then redirects to a
5
- * signed storage URL; none of them expires. */
4
+ type ThumbnailOwner = 'course' | 'download' | 'community' | 'event';
5
+ /** URLs for every file a response refers to. A public image (a profile photo, or the
6
+ * thumbnail or cover of a published item) is a public storage URL; every other file is a
7
+ * stable, access-checked URL on the API that redirects to a signed storage URL. */
6
8
  interface FileUrls {
9
+ publicImage(orgId: string, ownerId: string, assetId: string): string;
7
10
  thumbnail(owner: ThumbnailOwner, ownerId: string): string;
11
+ spaceImage(spaceId: string, image: SpaceImage): string;
8
12
  activityAsset(activityId: string, assetId: string): string;
9
13
  downloadFile(downloadId: string, assetId: string): string;
14
+ eventFile(eventId: string, assetId: string): string;
10
15
  postCover(postId: string): string;
16
+ postAsset(postId: string, assetId: string): string;
11
17
  messageAttachment(chatId: string, messageId: string, assetId: string): string;
12
- personImage(orgUserId: string): string;
13
18
  memberUpload(assetId: string): string;
14
19
  libraryFile(assetId: string): string;
15
20
  libraryDownload(assetId: string): string;
16
21
  }
17
- declare function createFileUrls(apiUrl: string): FileUrls;
22
+ declare function publicImagePrefix(orgId: string, ownerId: string): string;
23
+ declare function publicImageKey(orgId: string, ownerId: string, assetId: string): string;
24
+ declare function createFileUrls(apiUrl: string, publicUrl: (key: string) => string): FileUrls;
18
25
  //#endregion
19
- export { FileUrls, ThumbnailOwner, createFileUrls };
26
+ export { FileUrls, ThumbnailOwner, createFileUrls, publicImageKey, publicImagePrefix };
20
27
  //# sourceMappingURL=file-urls.d.ts.map
@@ -1,25 +1,36 @@
1
+ import { PUBLIC_PREFIX } from "../types/ports.js";
1
2
  //#region src/assets/file-urls.ts
2
3
  const THUMBNAIL_PATHS = {
3
4
  course: "courses",
4
5
  download: "downloads",
5
- community: "communities"
6
+ community: "communities",
7
+ event: "events"
6
8
  };
7
- function createFileUrls(apiUrl) {
9
+ function publicImagePrefix(orgId, ownerId) {
10
+ return `${PUBLIC_PREFIX}${orgId}/${ownerId}/`;
11
+ }
12
+ function publicImageKey(orgId, ownerId, assetId) {
13
+ return `${publicImagePrefix(orgId, ownerId)}${assetId}`;
14
+ }
15
+ function createFileUrls(apiUrl, publicUrl) {
8
16
  const base = apiUrl.replace(/\/+$/, "");
9
17
  const at = (...segments) => `${base}/api/${segments.map((s) => encodeURIComponent(s)).join("/")}`;
10
18
  return {
19
+ publicImage: (orgId, ownerId, assetId) => publicUrl(publicImageKey(orgId, ownerId, assetId)),
11
20
  thumbnail: (owner, ownerId) => at("member", THUMBNAIL_PATHS[owner], ownerId, "thumbnail"),
21
+ spaceImage: (spaceId, image) => at("member", "spaces", spaceId, "images", image),
12
22
  activityAsset: (activityId, assetId) => at("member", "activities", activityId, "assets", assetId),
13
23
  downloadFile: (downloadId, assetId) => at("member", "downloads", downloadId, "assets", assetId),
24
+ eventFile: (eventId, assetId) => at("member", "events", eventId, "assets", assetId),
14
25
  postCover: (postId) => at("member", "posts", postId, "cover"),
26
+ postAsset: (postId, assetId) => at("member", "posts", postId, "assets", assetId),
15
27
  messageAttachment: (chatId, messageId, assetId) => at("member", "chats", chatId, "messages", messageId, "attachments", assetId),
16
- personImage: (orgUserId) => at("member", "people", orgUserId, "image"),
17
28
  memberUpload: (assetId) => at("member", "uploads", assetId, "file"),
18
29
  libraryFile: (assetId) => at("assets", assetId, "file"),
19
30
  libraryDownload: (assetId) => at("assets", assetId, "download")
20
31
  };
21
32
  }
22
33
  //#endregion
23
- export { createFileUrls };
34
+ export { createFileUrls, publicImageKey, publicImagePrefix };
24
35
 
25
36
  //# sourceMappingURL=file-urls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-urls.js","names":[],"sources":["../../src/assets/file-urls.ts"],"sourcesContent":["export type ThumbnailOwner = 'course' | 'download' | 'community';\n\n/** Stable, access-checked URLs on the API for every file a response refers to.\n * Each one is answered by a route that checks access, then redirects to a\n * signed storage URL; none of them expires. */\nexport interface FileUrls {\n thumbnail(owner: ThumbnailOwner, ownerId: string): string;\n activityAsset(activityId: string, assetId: string): string;\n downloadFile(downloadId: string, assetId: string): string;\n postCover(postId: string): string;\n messageAttachment(chatId: string, messageId: string, assetId: string): string;\n personImage(orgUserId: string): string;\n memberUpload(assetId: string): string;\n libraryFile(assetId: string): string;\n libraryDownload(assetId: string): string;\n}\n\nconst THUMBNAIL_PATHS: Record<ThumbnailOwner, string> = {\n course: 'courses',\n download: 'downloads',\n community: 'communities',\n};\n\nexport function createFileUrls(apiUrl: string): FileUrls {\n const base = apiUrl.replace(/\\/+$/, '');\n const at = (...segments: string[]) =>\n `${base}/api/${segments.map((s) => encodeURIComponent(s)).join('/')}`;\n return {\n thumbnail: (owner, ownerId) => at('member', THUMBNAIL_PATHS[owner], ownerId, 'thumbnail'),\n activityAsset: (activityId, assetId) =>\n at('member', 'activities', activityId, 'assets', assetId),\n downloadFile: (downloadId, assetId) => at('member', 'downloads', downloadId, 'assets', assetId),\n postCover: (postId) => at('member', 'posts', postId, 'cover'),\n messageAttachment: (chatId, messageId, assetId) =>\n at('member', 'chats', chatId, 'messages', messageId, 'attachments', assetId),\n personImage: (orgUserId) => at('member', 'people', orgUserId, 'image'),\n memberUpload: (assetId) => at('member', 'uploads', assetId, 'file'),\n libraryFile: (assetId) => at('assets', assetId, 'file'),\n libraryDownload: (assetId) => at('assets', assetId, 'download'),\n };\n}\n"],"mappings":";AAiBA,MAAM,kBAAkD;CACtD,QAAQ;CACR,UAAU;CACV,WAAW;AACb;AAEA,SAAgB,eAAe,QAA0B;CACvD,MAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE;CACtC,MAAM,MAAM,GAAG,aACb,GAAG,KAAK,OAAO,SAAS,KAAK,MAAM,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;CACpE,OAAO;EACL,YAAY,OAAO,YAAY,GAAG,UAAU,gBAAgB,QAAQ,SAAS,WAAW;EACxF,gBAAgB,YAAY,YAC1B,GAAG,UAAU,cAAc,YAAY,UAAU,OAAO;EAC1D,eAAe,YAAY,YAAY,GAAG,UAAU,aAAa,YAAY,UAAU,OAAO;EAC9F,YAAY,WAAW,GAAG,UAAU,SAAS,QAAQ,OAAO;EAC5D,oBAAoB,QAAQ,WAAW,YACrC,GAAG,UAAU,SAAS,QAAQ,YAAY,WAAW,eAAe,OAAO;EAC7E,cAAc,cAAc,GAAG,UAAU,UAAU,WAAW,OAAO;EACrE,eAAe,YAAY,GAAG,UAAU,WAAW,SAAS,MAAM;EAClE,cAAc,YAAY,GAAG,UAAU,SAAS,MAAM;EACtD,kBAAkB,YAAY,GAAG,UAAU,SAAS,UAAU;CAChE;AACF"}
1
+ {"version":3,"file":"file-urls.js","names":[],"sources":["../../src/assets/file-urls.ts"],"sourcesContent":["import { PUBLIC_PREFIX, type SpaceImage } from '../types/index.js';\n\nexport type ThumbnailOwner = 'course' | 'download' | 'community' | 'event';\n\n/** URLs for every file a response refers to. A public image (a profile photo, or the\n * thumbnail or cover of a published item) is a public storage URL; every other file is a\n * stable, access-checked URL on the API that redirects to a signed storage URL. */\nexport interface FileUrls {\n publicImage(orgId: string, ownerId: string, assetId: string): string;\n thumbnail(owner: ThumbnailOwner, ownerId: string): string;\n spaceImage(spaceId: string, image: SpaceImage): string;\n activityAsset(activityId: string, assetId: string): string;\n downloadFile(downloadId: string, assetId: string): string;\n eventFile(eventId: string, assetId: string): string;\n postCover(postId: string): string;\n postAsset(postId: string, assetId: string): string;\n messageAttachment(chatId: string, messageId: string, assetId: string): string;\n memberUpload(assetId: string): string;\n libraryFile(assetId: string): string;\n libraryDownload(assetId: string): string;\n}\n\nconst THUMBNAIL_PATHS: Record<ThumbnailOwner, string> = {\n course: 'courses',\n download: 'downloads',\n community: 'communities',\n event: 'events',\n};\n\nexport function publicImagePrefix(orgId: string, ownerId: string): string {\n return `${PUBLIC_PREFIX}${orgId}/${ownerId}/`;\n}\n\nexport function publicImageKey(orgId: string, ownerId: string, assetId: string): string {\n return `${publicImagePrefix(orgId, ownerId)}${assetId}`;\n}\n\nexport function createFileUrls(apiUrl: string, publicUrl: (key: string) => string): FileUrls {\n const base = apiUrl.replace(/\\/+$/, '');\n const at = (...segments: string[]) =>\n `${base}/api/${segments.map((s) => encodeURIComponent(s)).join('/')}`;\n return {\n publicImage: (orgId, ownerId, assetId) => publicUrl(publicImageKey(orgId, ownerId, assetId)),\n thumbnail: (owner, ownerId) => at('member', THUMBNAIL_PATHS[owner], ownerId, 'thumbnail'),\n spaceImage: (spaceId, image) => at('member', 'spaces', spaceId, 'images', image),\n activityAsset: (activityId, assetId) =>\n at('member', 'activities', activityId, 'assets', assetId),\n downloadFile: (downloadId, assetId) => at('member', 'downloads', downloadId, 'assets', assetId),\n eventFile: (eventId, assetId) => at('member', 'events', eventId, 'assets', assetId),\n postCover: (postId) => at('member', 'posts', postId, 'cover'),\n postAsset: (postId, assetId) => at('member', 'posts', postId, 'assets', assetId),\n messageAttachment: (chatId, messageId, assetId) =>\n at('member', 'chats', chatId, 'messages', messageId, 'attachments', assetId),\n memberUpload: (assetId) => at('member', 'uploads', assetId, 'file'),\n libraryFile: (assetId) => at('assets', assetId, 'file'),\n libraryDownload: (assetId) => at('assets', assetId, 'download'),\n };\n}\n"],"mappings":";;AAsBA,MAAM,kBAAkD;CACtD,QAAQ;CACR,UAAU;CACV,WAAW;CACX,OAAO;AACT;AAEA,SAAgB,kBAAkB,OAAe,SAAyB;CACxE,OAAO,GAAG,gBAAgB,MAAM,GAAG,QAAQ;AAC7C;AAEA,SAAgB,eAAe,OAAe,SAAiB,SAAyB;CACtF,OAAO,GAAG,kBAAkB,OAAO,OAAO,IAAI;AAChD;AAEA,SAAgB,eAAe,QAAgB,WAA8C;CAC3F,MAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE;CACtC,MAAM,MAAM,GAAG,aACb,GAAG,KAAK,OAAO,SAAS,KAAK,MAAM,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;CACpE,OAAO;EACL,cAAc,OAAO,SAAS,YAAY,UAAU,eAAe,OAAO,SAAS,OAAO,CAAC;EAC3F,YAAY,OAAO,YAAY,GAAG,UAAU,gBAAgB,QAAQ,SAAS,WAAW;EACxF,aAAa,SAAS,UAAU,GAAG,UAAU,UAAU,SAAS,UAAU,KAAK;EAC/E,gBAAgB,YAAY,YAC1B,GAAG,UAAU,cAAc,YAAY,UAAU,OAAO;EAC1D,eAAe,YAAY,YAAY,GAAG,UAAU,aAAa,YAAY,UAAU,OAAO;EAC9F,YAAY,SAAS,YAAY,GAAG,UAAU,UAAU,SAAS,UAAU,OAAO;EAClF,YAAY,WAAW,GAAG,UAAU,SAAS,QAAQ,OAAO;EAC5D,YAAY,QAAQ,YAAY,GAAG,UAAU,SAAS,QAAQ,UAAU,OAAO;EAC/E,oBAAoB,QAAQ,WAAW,YACrC,GAAG,UAAU,SAAS,QAAQ,YAAY,WAAW,eAAe,OAAO;EAC7E,eAAe,YAAY,GAAG,UAAU,WAAW,SAAS,MAAM;EAClE,cAAc,YAAY,GAAG,UAAU,SAAS,MAAM;EACtD,kBAAkB,YAAY,GAAG,UAAU,SAAS,UAAU;CAChE;AACF"}
@@ -1,9 +1,9 @@
1
1
  import { Page } from "../types/shared.js";
2
2
  import { Asset, AssetKind, AssetSource, AssetStatus, AssetUrls, AssetView, AssetsQuery, RequestUploadInput, UploadTicket } from "../types/schemas/assets.js";
3
3
  import { AssetCreated, AssetDeleted, AssetEvent, AssetReady, assetEvents } from "./events.js";
4
- import { FileUrls, ThumbnailOwner, createFileUrls } from "./file-urls.js";
4
+ import { FileUrls, ThumbnailOwner, createFileUrls, publicImageKey, publicImagePrefix } from "./file-urls.js";
5
5
  import "./model.js";
6
- import { AssetFiles, AssetsRepository, AssetsService, LibraryAssetReader, MemberImageReader, MemberUploadInput, OrgDirectory, ReadyFile } from "./ports.js";
7
- import { AssetsServiceImpl, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf } from "./service.js";
6
+ import { AssetFiles, AssetsRepository, AssetsService, LibraryAssetReader, MemberImageReader, MemberUpload, MemberUploadInput, MemberUploadReader, OrgDirectory, ReadyFile } from "./ports.js";
7
+ import { AssetsServiceImpl, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf, memberKindOf } from "./service.js";
8
8
  import { UPLOADS_REMOVE_UNATTACHED_JOB, assetsJobs, assetsRecurringJobs } from "./jobs.js";
9
- export { type Asset, type AssetCreated, type AssetDeleted, type AssetEvent, type AssetFiles, type AssetKind, type AssetReady, type AssetSource, type AssetStatus, type AssetUrls, type AssetView, type AssetsQuery, type AssetsRepository, type AssetsService, AssetsServiceImpl, type FileUrls, type LibraryAssetReader, MEMBER_IMAGE_TYPE, type MemberImageReader, type MemberUploadInput, type OrgDirectory, type Page, type ReadyFile, type RequestUploadInput, type ThumbnailOwner, UPLOADS_REMOVE_UNATTACHED_JOB, type UploadTicket, assetEvents, assetsJobs, assetsRecurringJobs, createFileUrls, isMemberImageType, kindOf };
9
+ export { type Asset, type AssetCreated, type AssetDeleted, type AssetEvent, type AssetFiles, type AssetKind, type AssetReady, type AssetSource, type AssetStatus, type AssetUrls, type AssetView, type AssetsQuery, type AssetsRepository, type AssetsService, AssetsServiceImpl, type FileUrls, type LibraryAssetReader, MEMBER_IMAGE_TYPE, type MemberImageReader, type MemberUpload, type MemberUploadInput, type MemberUploadReader, type OrgDirectory, type Page, type ReadyFile, type RequestUploadInput, type ThumbnailOwner, UPLOADS_REMOVE_UNATTACHED_JOB, type UploadTicket, assetEvents, assetsJobs, assetsRecurringJobs, createFileUrls, isMemberImageType, kindOf, memberKindOf, publicImageKey, publicImagePrefix };
@@ -1,5 +1,5 @@
1
1
  import { assetEvents } from "./events.js";
2
- import { createFileUrls } from "./file-urls.js";
3
- import { AssetsServiceImpl, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf } from "./service.js";
2
+ import { createFileUrls, publicImageKey, publicImagePrefix } from "./file-urls.js";
3
+ import { AssetsServiceImpl, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf, memberKindOf } from "./service.js";
4
4
  import { UPLOADS_REMOVE_UNATTACHED_JOB, assetsJobs, assetsRecurringJobs } from "./jobs.js";
5
- export { AssetsServiceImpl, MEMBER_IMAGE_TYPE, UPLOADS_REMOVE_UNATTACHED_JOB, assetEvents, assetsJobs, assetsRecurringJobs, createFileUrls, isMemberImageType, kindOf };
5
+ export { AssetsServiceImpl, MEMBER_IMAGE_TYPE, UPLOADS_REMOVE_UNATTACHED_JOB, assetEvents, assetsJobs, assetsRecurringJobs, createFileUrls, isMemberImageType, kindOf, memberKindOf, publicImageKey, publicImagePrefix };
@@ -1,13 +1,30 @@
1
1
  import { Page } from "../types/shared.js";
2
- import { Asset, AssetView, AssetsQuery, RequestUploadInput, UploadTicket } from "../types/schemas/assets.js";
2
+ import { ContentType } from "../types/schemas/content.js";
3
+ import { Asset, AssetKind, AssetView, AssetsQuery, RequestUploadInput, UploadTicket } from "../types/schemas/assets.js";
4
+ import "../types/index.js";
5
+ import { Actor } from "../shared/actor.js";
3
6
  import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
4
7
  import "./model.js";
5
8
  //#region src/assets/ports.d.ts
6
9
  interface MemberImageReader {
7
10
  ownsImage(orgId: string, assetId: string, orgUserId: string): Promise<boolean>;
8
11
  }
12
+ interface MemberUpload {
13
+ id: string;
14
+ kind: AssetKind;
15
+ filename: string;
16
+ contentType: string;
17
+ size: number;
18
+ }
19
+ interface MemberUploadReader {
20
+ ownsUpload(orgId: string, assetId: string, orgUserId: string): Promise<boolean>;
21
+ memberUploads(orgId: string, assetIds: string[]): Promise<MemberUpload[]>;
22
+ }
9
23
  interface LibraryAssetReader {
10
- libraryAssetsAmong(orgId: string, assetIds: string[]): Promise<string[]>;
24
+ libraryAssetsFor(orgId: string, assetIds: string[]): Promise<{
25
+ id: string;
26
+ contentId: string | null;
27
+ }[]>;
11
28
  }
12
29
  interface ReadyFile {
13
30
  filename: string;
@@ -22,24 +39,25 @@ interface AssetFiles {
22
39
  signFile(orgId: string, assetId: string, opts?: {
23
40
  saveAs?: string;
24
41
  }): Promise<string | null>;
42
+ syncPublicImages(orgId: string, ownerId: string, assetIds: readonly string[]): Promise<void>;
25
43
  }
26
44
  interface MemberUploadInput {
27
45
  filename: string;
28
46
  contentType: string;
29
47
  }
30
- interface AssetsService extends MemberImageReader, LibraryAssetReader, AssetFiles {
31
- requestUpload(orgId: string, input: RequestUploadInput): Promise<UploadTicket>;
32
- requestMemberUpload(orgId: string, orgUserId: string, input: MemberUploadInput): Promise<UploadTicket>;
33
- confirm(orgId: string, id: string): Promise<AssetView | null>;
34
- confirmOwn(orgId: string, id: string, orgUserId: string): Promise<AssetView | null>;
35
- list(orgId: string, query: AssetsQuery): Promise<Page<AssetView>>;
36
- get(orgId: string, id: string): Promise<AssetView | null>;
37
- libraryFile(orgId: string, assetId: string, opts: {
48
+ interface AssetsService extends MemberImageReader, MemberUploadReader, LibraryAssetReader, AssetFiles {
49
+ requestUpload(input: RequestUploadInput, actor: Actor): Promise<UploadTicket>;
50
+ requestMemberUpload(input: MemberUploadInput, actor: Actor): Promise<UploadTicket>;
51
+ confirm(id: string, actor: Actor): Promise<AssetView>;
52
+ confirmOwn(id: string, actor: Actor): Promise<AssetView>;
53
+ list(query: AssetsQuery, actor: Actor): Promise<Page<AssetView>>;
54
+ get(id: string, actor: Actor): Promise<AssetView>;
55
+ libraryFile(assetId: string, opts: {
38
56
  download: boolean;
39
- }): Promise<string | null>;
40
- ownUploadFile(orgId: string, assetId: string, orgUserId: string): Promise<string | null>;
41
- remove(orgId: string, id: string): Promise<boolean>;
42
- removeOwn(orgId: string, id: string, orgUserId: string): Promise<boolean>;
57
+ }, actor: Actor): Promise<string>;
58
+ ownUploadFile(assetId: string, actor: Actor): Promise<string>;
59
+ remove(id: string, actor: Actor): Promise<void>;
60
+ removeOwn(id: string, actor: Actor): Promise<void>;
43
61
  removeUnattached(orgId: string, now: Date): Promise<number>;
44
62
  }
45
63
  interface OrgDirectory {
@@ -54,6 +72,7 @@ interface AssetsRepository {
54
72
  delete(orgId: string, id: string): Promise<boolean>;
55
73
  listUnreferencedMemberUploads(orgId: string, before: Date): Promise<Asset[]>;
56
74
  heldKeys(orgId: string, keys: string[]): Promise<string[]>;
75
+ contentTypeOf(orgId: string, contentId: string): Promise<ContentType | null>;
57
76
  }
58
77
  interface AssetsWriteScope {
59
78
  assets: AssetsRepository;
@@ -61,5 +80,5 @@ interface AssetsWriteScope {
61
80
  }
62
81
  type AssetsUnitOfWork = UnitOfWork<AssetsWriteScope>;
63
82
  //#endregion
64
- export { AssetFiles, AssetsRepository, AssetsService, AssetsUnitOfWork, AssetsWriteScope, LibraryAssetReader, MemberImageReader, MemberUploadInput, OrgDirectory, ReadyFile };
83
+ export { AssetFiles, AssetsRepository, AssetsService, AssetsUnitOfWork, AssetsWriteScope, LibraryAssetReader, MemberImageReader, MemberUpload, MemberUploadInput, MemberUploadReader, OrgDirectory, ReadyFile };
65
84
  //# sourceMappingURL=ports.d.ts.map
@@ -1,19 +1,22 @@
1
1
  import { Page } from "../types/shared.js";
2
2
  import { Logger, ObjectStorage } from "../types/ports.js";
3
3
  import { AssetKind, AssetView, AssetsQuery, RequestUploadInput, UploadTicket } from "../types/schemas/assets.js";
4
+ import { Actor } from "../shared/actor.js";
4
5
  import "../shared/ports.js";
5
6
  import { FileUrls } from "./file-urls.js";
6
7
  import "./model.js";
7
- import { AssetsRepository, AssetsService, AssetsUnitOfWork, MemberUploadInput, ReadyFile } from "./ports.js";
8
+ import { AssetsRepository, AssetsService, AssetsUnitOfWork, MemberUpload, MemberUploadInput, ReadyFile } from "./ports.js";
8
9
  //#region src/assets/service.d.ts
9
10
  declare const MEMBER_IMAGE_TYPE: RegExp;
10
11
  declare function isMemberImageType(contentType: string): boolean;
12
+ declare function memberKindOf(contentType: string): AssetKind;
11
13
  declare function kindOf(contentType: string): AssetKind;
12
14
  type AssetsServiceParams = {
13
15
  storage: ObjectStorage;
14
16
  repo: AssetsRepository;
15
17
  uow: AssetsUnitOfWork;
16
18
  memberImageMaxBytes: number;
19
+ memberFileMaxBytes: number;
17
20
  downloadLinkExpirySeconds: number;
18
21
  fileUrls: FileUrls;
19
22
  logger?: Logger;
@@ -23,29 +26,41 @@ declare class AssetsServiceImpl implements AssetsService {
23
26
  private readonly repo;
24
27
  private readonly uow;
25
28
  private readonly memberImageMaxBytes;
29
+ private readonly memberFileMaxBytes;
26
30
  private readonly downloadLinkExpirySeconds;
27
31
  private readonly fileUrls;
28
32
  private readonly logger;
29
33
  constructor(params: AssetsServiceParams);
30
- requestUpload(orgId: string, input: RequestUploadInput): Promise<UploadTicket>;
31
- requestMemberUpload(orgId: string, orgUserId: string, input: MemberUploadInput): Promise<UploadTicket>;
32
- confirm(orgId: string, id: string): Promise<AssetView | null>;
33
- confirmOwn(orgId: string, id: string, orgUserId: string): Promise<AssetView | null>;
34
- ownUploadFile(orgId: string, assetId: string, orgUserId: string): Promise<string | null>;
34
+ requestUpload(input: RequestUploadInput, actor: Actor): Promise<UploadTicket>;
35
+ requestMemberUpload(input: MemberUploadInput, actor: Actor): Promise<UploadTicket>;
36
+ private createUpload;
37
+ confirm(id: string, actor: Actor): Promise<AssetView>;
38
+ confirmOwn(id: string, actor: Actor): Promise<AssetView>;
39
+ ownUploadFile(assetId: string, actor: Actor): Promise<string>;
40
+ ownsUpload(orgId: string, assetId: string, orgUserId: string): Promise<boolean>;
41
+ memberUploads(orgId: string, assetIds: string[]): Promise<MemberUpload[]>;
35
42
  ownsImage(orgId: string, assetId: string, orgUserId: string): Promise<boolean>;
36
- libraryAssetsAmong(orgId: string, assetIds: string[]): Promise<string[]>;
37
- list(orgId: string, query: AssetsQuery): Promise<Page<AssetView>>;
38
- get(orgId: string, id: string): Promise<AssetView | null>;
43
+ libraryAssetsFor(orgId: string, assetIds: string[]): Promise<{
44
+ id: string;
45
+ contentId: string | null;
46
+ }[]>;
47
+ list(query: AssetsQuery, actor: Actor): Promise<Page<AssetView>>;
48
+ get(id: string, actor: Actor): Promise<AssetView>;
39
49
  readyFiles(orgId: string, assetIds: readonly string[]): Promise<Record<string, ReadyFile>>;
40
50
  signFile(orgId: string, assetId: string, opts?: {
41
51
  saveAs?: string;
42
52
  }): Promise<string | null>;
43
- libraryFile(orgId: string, assetId: string, opts: {
53
+ syncPublicImages(orgId: string, ownerId: string, assetIds: readonly string[]): Promise<void>;
54
+ libraryFile(assetId: string, opts: {
44
55
  download: boolean;
45
- }): Promise<string | null>;
46
- remove(orgId: string, id: string): Promise<boolean>;
47
- removeOwn(orgId: string, id: string, orgUserId: string): Promise<boolean>;
56
+ }, actor: Actor): Promise<string>;
57
+ remove(id: string, actor: Actor): Promise<void>;
58
+ removeOwn(id: string, actor: Actor): Promise<void>;
59
+ private removeAsset;
60
+ private requireReach;
61
+ private requireItemWrite;
48
62
  removeUnattached(orgId: string, now: Date): Promise<number>;
63
+ private orphanPublicImages;
49
64
  private strayUploads;
50
65
  private finalize;
51
66
  private finalizeMember;
@@ -53,8 +68,9 @@ declare class AssetsServiceImpl implements AssetsService {
53
68
  private readyAmong;
54
69
  private view;
55
70
  private discard;
71
+ private removePublicCopies;
56
72
  private removeObject;
57
73
  }
58
74
  //#endregion
59
- export { AssetsServiceImpl, AssetsServiceParams, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf };
75
+ export { AssetsServiceImpl, AssetsServiceParams, MEMBER_IMAGE_TYPE, isMemberImageType, kindOf, memberKindOf };
60
76
  //# sourceMappingURL=service.d.ts.map