@commonpub/layer 0.7.0 → 0.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -52,14 +52,14 @@
52
52
  "zod": "^4.3.6",
53
53
  "@commonpub/auth": "0.5.0",
54
54
  "@commonpub/config": "0.8.0",
55
- "@commonpub/docs": "0.6.0",
56
- "@commonpub/explainer": "0.7.1",
57
55
  "@commonpub/editor": "0.5.0",
58
56
  "@commonpub/learning": "0.5.0",
59
- "@commonpub/protocol": "0.9.7",
60
- "@commonpub/schema": "0.9.0",
61
- "@commonpub/server": "2.27.0",
62
- "@commonpub/ui": "0.8.4"
57
+ "@commonpub/docs": "0.6.1",
58
+ "@commonpub/schema": "0.9.1",
59
+ "@commonpub/ui": "0.8.4",
60
+ "@commonpub/explainer": "0.7.2",
61
+ "@commonpub/server": "2.27.1",
62
+ "@commonpub/protocol": "0.9.7"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@testing-library/jest-dom": "^6.9.1",
@@ -205,13 +205,20 @@ async function handleDiscPost(): Promise<void> {
205
205
  // --- Instance mirror status ---
206
206
  const mirrorStatus = computed(() => hub.value?.followStatus ?? 'pending');
207
207
 
208
- // --- Per-user join state ---
209
- const { data: userFollowState, refresh: refreshFollowState } = useLazyFetch<{ joined: boolean; status: string | null }>(
210
- () => `/api/federation/hub-follow-status?federatedHubId=${id}`,
211
- { default: () => ({ joined: false, status: null }) },
212
- );
208
+ // --- Per-user join state (only fetch when authenticated) ---
209
+ const userFollowState = ref<{ joined: boolean; status: string | null }>({ joined: false, status: null });
213
210
  const userJoined = computed(() => userFollowState.value?.joined ?? false);
214
211
 
212
+ async function refreshFollowState(): Promise<void> {
213
+ if (!isAuthenticated.value) return;
214
+ try {
215
+ userFollowState.value = await $fetch<{ joined: boolean; status: string | null }>(
216
+ `/api/federation/hub-follow-status?federatedHubId=${id}`,
217
+ );
218
+ } catch { /* best-effort */ }
219
+ }
220
+ onMounted(() => { refreshFollowState(); });
221
+
215
222
  const remoteFollowRef = ref<{ show: () => void } | null>(null);
216
223
  const hubFollowing = ref(false);
217
224
  const hubFollowStatus = computed(() => hub.value?.followStatus ?? '');
@@ -19,16 +19,8 @@ export default defineEventHandler(async (event) => {
19
19
  const page = pages.find((p) => p.slug === pageSlug);
20
20
  if (!page) throw createError({ statusCode: 404, statusMessage: 'Page not found' });
21
21
 
22
- // Handle dual-format content: BlockTuple[] (new) or markdown string (legacy)
23
- // Content is stored as TEXT — JSON arrays come back as strings, need parsing
24
- let content: string | unknown[] = page.content ?? '';
25
- if (typeof content === 'string' && content.trimStart().startsWith('[')) {
26
- try {
27
- content = JSON.parse(content);
28
- } catch {
29
- // Not valid JSON — keep as markdown string
30
- }
31
- }
22
+ // Content is JSONB arrays come back parsed, legacy strings stay as strings
23
+ const content = page.content ?? '';
32
24
 
33
25
  if (Array.isArray(content)) {
34
26
  // New BlockTuple format — extract text for TOC generation
@@ -32,16 +32,9 @@ export default defineEventHandler(async (event) => {
32
32
 
33
33
  const pages = await listDocsPages(db, version.id);
34
34
 
35
- // Parse content: if stored as JSON string (BlockTuple[]), parse back to array
35
+ // Content is JSONB arrays come back parsed, legacy strings stay as strings
36
36
  return pages.map((page) => {
37
- let content: string | unknown[] = page.content ?? '';
38
- if (typeof content === 'string' && content.startsWith('[')) {
39
- try {
40
- content = JSON.parse(content);
41
- } catch {
42
- // Not valid JSON — keep as markdown string
43
- }
44
- }
37
+ const content = page.content ?? '';
45
38
  return { ...page, content };
46
39
  });
47
40
  });
@@ -68,8 +68,7 @@ export default defineEventHandler(async (event) => {
68
68
  const ext = contentType.split('/')[1] || 'jpg';
69
69
  const key = generateStorageKey(purpose, ext);
70
70
 
71
- await (storage as any).put(key, buffer, contentType);
72
- const resultUrl = (storage as any).getPublicUrl(key);
71
+ const resultUrl = await storage.upload(key, buffer, contentType);
73
72
 
74
73
  return { url: resultUrl };
75
74
  });
@@ -76,6 +76,9 @@ export default defineNitroPlugin((nitro) => {
76
76
  }
77
77
  }, 5_000);
78
78
 
79
+ // Track the last date digests were sent to prevent duplicates on server restart during 8am hour
80
+ let lastDigestDate = '';
81
+
79
82
  async function runDigest(siteUrl: string, siteName: string): Promise<void> {
80
83
  try {
81
84
  const db = useDB();
@@ -88,6 +91,11 @@ export default defineNitroPlugin((nitro) => {
88
91
 
89
92
  if (!isDigestHour) return;
90
93
 
94
+ // Prevent duplicate sends if server restarts during digest hour
95
+ const todayKey = now.toISOString().slice(0, 10);
96
+ if (lastDigestDate === todayKey) return;
97
+ lastDigestDate = todayKey;
98
+
91
99
  // Find users with digest preferences
92
100
  const digestUsers = await db
93
101
  .select({