@abraca/nuxt 2.20.0 → 2.22.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.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "2.20.0",
7
+ "version": "2.22.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -124,7 +124,13 @@ const module$1 = defineNuxtModule({
124
124
  abracadabra: {
125
125
  servicePublicKey: options.service?.publicKey ?? "",
126
126
  servicePrivateKey: options.service?.privateKey ?? "",
127
- serviceRootDocId: options.service?.rootDocId ?? "",
127
+ // Default the service root to the app's entry doc. The previous
128
+ // fallback (the backend's /info root_doc_id) is the all-zeros server
129
+ // WORLD root on multi-space servers — the service then synced an
130
+ // empty doc-tree: 0 slugs, empty SSR cache, no server rendering.
131
+ // An explicit service.rootDocId still wins; /info discovery remains
132
+ // the last resort inside the service plugin when both are unset.
133
+ serviceRootDocId: options.service?.rootDocId ?? options.entryDocId ?? "",
128
134
  serviceDisabled: options.service?.disabled ?? false
129
135
  }
130
136
  });
@@ -239,8 +239,8 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import
239
239
  autofocus: boolean;
240
240
  loading: boolean;
241
241
  dismissible: boolean;
242
- overlay: boolean;
243
242
  colorMode: boolean;
243
+ overlay: boolean;
244
244
  fullscreen: boolean;
245
245
  modal: boolean;
246
246
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>, __VLS_Slots>;
@@ -239,8 +239,8 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import
239
239
  autofocus: boolean;
240
240
  loading: boolean;
241
241
  dismissible: boolean;
242
- overlay: boolean;
243
242
  colorMode: boolean;
243
+ overlay: boolean;
244
244
  fullscreen: boolean;
245
245
  modal: boolean;
246
246
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>, __VLS_Slots>;
@@ -23,7 +23,7 @@ export function useDocTree() {
23
23
  };
24
24
  const slugMap = computed(() => buildSlugMap(entries.value));
25
25
  const navigationItems = computed(
26
- () => topLevelDocs.value.map((e) => {
26
+ () => topLevelDocs.value.filter((e) => e.meta?.hidden !== true).map((e) => {
27
27
  const slug = slugMap.value.idToSlug[e.id];
28
28
  const path = slug ?? e.id;
29
29
  return {
@@ -15,7 +15,7 @@ function extractFirstImage(html) {
15
15
  return match?.[1] ?? "";
16
16
  }
17
17
  export default defineEventHandler(async (event) => {
18
- const slugParam = getRouterParam(event, "slug");
18
+ const slugParam = getRouterParam(event, "slug") ?? event.context.params?._;
19
19
  if (!slugParam) {
20
20
  throw createError({ statusCode: 400, message: "slug is required" });
21
21
  }
@@ -14,6 +14,7 @@ import type { DocCacheAPI, NitroStorage } from '../../../runtime/types.js';
14
14
  * Must be called before observeDoc().
15
15
  */
16
16
  export declare function initDocCache(storage: NitroStorage, extraExtensions?: AnyExtension[]): void;
17
+ export declare function renderDocToHTML(ydoc: Y.Doc): Promise<string | null>;
17
18
  export declare function getCachedHTML(docId: string): Promise<string | null>;
18
19
  export declare function getCachedJSON(docId: string): Promise<Record<string, unknown> | null>;
19
20
  export declare function invalidateDoc(docId: string, ydoc?: Y.Doc): Promise<void>;
@@ -18,16 +18,21 @@ async function getServerDoc() {
18
18
  _serverDoc = new Window().document;
19
19
  return _serverDoc;
20
20
  }
21
- async function renderDocToHTML(ydoc) {
21
+ export async function renderDocToHTML(ydoc) {
22
22
  try {
23
- const [{ getSchema }, { yDocToProsemirrorJSON }, { default: StarterKit }, { Node, DOMSerializer }] = await Promise.all([
23
+ const [{ getSchema }, { yDocToProsemirrorJSON }, { default: StarterKit }, { Node, DOMSerializer }, { coreServerSchemaStubs, pruneUnknownNodes }] = await Promise.all([
24
24
  import("@tiptap/core"),
25
25
  import("@tiptap/y-tiptap"),
26
26
  import("@tiptap/starter-kit"),
27
- import("prosemirror-model")
27
+ import("prosemirror-model"),
28
+ import("./serverSchemaStubs.js")
28
29
  ]);
29
- const schema = getSchema([StarterKit, ..._extensions]);
30
- const json = yDocToProsemirrorJSON(ydoc, "default");
30
+ const schema = getSchema([
31
+ StarterKit,
32
+ ...coreServerSchemaStubs(_extensions),
33
+ ..._extensions
34
+ ]);
35
+ const json = pruneUnknownNodes(yDocToProsemirrorJSON(ydoc, "default"), schema);
31
36
  const node = Node.fromJSON(schema, json);
32
37
  const serverDoc = await getServerDoc();
33
38
  const container = serverDoc.createElement("div");
@@ -0,0 +1,23 @@
1
+ import type { AnyExtension } from '@tiptap/core';
2
+ import type { Schema } from 'prosemirror-model';
3
+ /**
4
+ * Stubs for the doc-cache schema. Any stub whose node name is already
5
+ * provided by the app's `serverExtensions()` is skipped — apps with richer
6
+ * server definitions win.
7
+ */
8
+ export declare function coreServerSchemaStubs(appExtensions?: AnyExtension[]): AnyExtension[];
9
+ interface PmJsonNode {
10
+ type?: string;
11
+ content?: PmJsonNode[];
12
+ marks?: Array<{
13
+ type?: string;
14
+ }>;
15
+ [key: string]: unknown;
16
+ }
17
+ /**
18
+ * Drop nodes whose type the schema doesn't know (and unknown marks), keeping
19
+ * everything else intact. Returns a structurally-cloned JSON tree that is
20
+ * safe to feed into `Node.fromJSON`.
21
+ */
22
+ export declare function pruneUnknownNodes<T extends PmJsonNode>(json: T, schema: Schema): T;
23
+ export {};
@@ -0,0 +1,50 @@
1
+ import { Node } from "@tiptap/core";
2
+ const DocumentHeaderStub = Node.create({
3
+ name: "documentHeader",
4
+ group: "block",
5
+ content: "inline*",
6
+ renderHTML: () => ["h1", 0]
7
+ });
8
+ const DocumentMetaStub = Node.create({
9
+ name: "documentMeta",
10
+ group: "block",
11
+ atom: true,
12
+ renderHTML: () => ["div", { "data-document-meta": "", "style": "display:none" }]
13
+ });
14
+ const CalloutStub = Node.create({
15
+ name: "callout",
16
+ group: "block",
17
+ content: "block+",
18
+ renderHTML: () => ["div", { class: "callout" }, 0]
19
+ });
20
+ const DividerStub = Node.create({
21
+ name: "divider",
22
+ group: "block",
23
+ atom: true,
24
+ renderHTML: () => ["hr"]
25
+ });
26
+ const QuoteStub = Node.create({
27
+ name: "quote",
28
+ group: "block",
29
+ content: "block+",
30
+ renderHTML: () => ["blockquote", 0]
31
+ });
32
+ export function coreServerSchemaStubs(appExtensions = []) {
33
+ const taken = new Set(appExtensions.map((e) => e.name));
34
+ return [DocumentHeaderStub, DocumentMetaStub, CalloutStub, DividerStub, QuoteStub].filter((s) => !taken.has(s.name));
35
+ }
36
+ export function pruneUnknownNodes(json, schema) {
37
+ const knownNode = (t) => !!t && !!schema.nodes[t];
38
+ const knownMark = (t) => !!t && !!schema.marks[t];
39
+ const walk = (node) => {
40
+ const out = { ...node };
41
+ if (Array.isArray(node.marks)) {
42
+ out.marks = node.marks.filter((m) => knownMark(m?.type));
43
+ }
44
+ if (Array.isArray(node.content)) {
45
+ out.content = node.content.filter((child) => child?.type === "text" || knownNode(child?.type)).map(walk);
46
+ }
47
+ return out;
48
+ };
49
+ return walk(json);
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/nuxt",
3
- "version": "2.20.0",
3
+ "version": "2.22.0",
4
4
  "description": "First-class Nuxt module for the Abracadabra CRDT collaboration platform",
5
5
  "repository": "abracadabra/abracadabra-nuxt",
6
6
  "license": "MIT",