From 800533e10bb82259e9b25c62e8768fe832717fc8 Mon Sep 17 00:00:00 2001 From: Uwe Heinrichs Date: Fri, 17 Apr 2026 15:28:51 +0200 Subject: [PATCH 1/3] fix(preview): skip collections without source in preview template The internal `info` collection (created by `resolveCollections()`) has `source: undefined`. The previous `collection.source?.filter(...) || []` fallback produced `source: []` in the serialized preview payload, which violates the `ResolvedCollectionSource[]` contract and crashes downstream consumers (nuxt-studio) that access `source[0].include` and `source[0].prefix`. The `info` collection is internal and cannot be edited from the preview/studio UI, so it should not appear in the preview manifest at all. This skips such collections entirely and also simplifies the remote-collection filter to a straightforward `!source.repository` predicate. Resolves #3767 --- src/utils/templates.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/templates.ts b/src/utils/templates.ts index f9e0cc447..b15c981d4 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -206,6 +206,14 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI filename: moduleTemplates.preview, getContents: ({ options }: { options: { collections: ResolvedCollection[] } }) => { const collectionsMeta = options.collections.reduce((acc, collection) => { + // Skip collections without a source (e.g. the internal `info` collection). + // They cannot be edited from the preview/studio UI, and serializing them + // with an empty `source: []` array breaks downstream consumers that expect + // at least one `ResolvedCollectionSource` entry. + if (!collection.source) { + return acc + } + const schemaWithCollectionName = { ...collection.extendedSchema, definitions: { @@ -217,7 +225,7 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI pascalName: pascalCase(collection.name), tableName: collection.tableName, // Remove source from collection meta if it's a remote collection - source: collection.source?.filter(source => source.repository ? undefined : collection.source) || [], + source: collection.source.filter(source => !source.repository), type: collection.type, fields: collection.fields, schema: schemaWithCollectionName, From cba6554cc3049ea2059cecbf00217d3059237001 Mon Sep 17 00:00:00 2001 From: Uwe Heinrichs Date: Fri, 17 Apr 2026 15:44:15 +0200 Subject: [PATCH 2/3] fix(preview): also skip remote-only collections CodeRabbit flagged that filtering out remote sources could still produce `source: []` when a collection's sources are *all* remote. Compute the filtered local sources first and skip the collection entirely if nothing remains, so the invariant "if a collection appears in the preview manifest, it has at least one local source" holds for both the no-source case (e.g. the internal `info` collection) and the remote-only case. --- src/utils/templates.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/utils/templates.ts b/src/utils/templates.ts index b15c981d4..66e58c3f2 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -206,11 +206,15 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI filename: moduleTemplates.preview, getContents: ({ options }: { options: { collections: ResolvedCollection[] } }) => { const collectionsMeta = options.collections.reduce((acc, collection) => { - // Skip collections without a source (e.g. the internal `info` collection). - // They cannot be edited from the preview/studio UI, and serializing them - // with an empty `source: []` array breaks downstream consumers that expect - // at least one `ResolvedCollectionSource` entry. - if (!collection.source) { + // Skip collections that would be serialized with an empty `source` array, + // because downstream consumers (e.g. nuxt-studio) iterate `source[0]` and + // crash on empty arrays. This covers two cases: + // 1. Collections with no source at all (e.g. the internal `info` collection). + // 2. Collections whose sources are all remote repositories — those are + // filtered out below and should not appear in the preview manifest, + // since they cannot be edited from the preview/studio UI either. + const localSources = collection.source?.filter(source => !source.repository) ?? [] + if (localSources.length === 0) { return acc } @@ -224,8 +228,7 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI name: collection.name, pascalName: pascalCase(collection.name), tableName: collection.tableName, - // Remove source from collection meta if it's a remote collection - source: collection.source.filter(source => !source.repository), + source: localSources, type: collection.type, fields: collection.fields, schema: schemaWithCollectionName, From 2f7254ef5b515bf7758f8b34873b24a68b7f9c1e Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Wed, 22 Apr 2026 16:20:15 +0200 Subject: [PATCH 3/3] up comment --- src/utils/templates.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/utils/templates.ts b/src/utils/templates.ts index 66e58c3f2..473a32c5f 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -206,13 +206,7 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI filename: moduleTemplates.preview, getContents: ({ options }: { options: { collections: ResolvedCollection[] } }) => { const collectionsMeta = options.collections.reduce((acc, collection) => { - // Skip collections that would be serialized with an empty `source` array, - // because downstream consumers (e.g. nuxt-studio) iterate `source[0]` and - // crash on empty arrays. This covers two cases: - // 1. Collections with no source at all (e.g. the internal `info` collection). - // 2. Collections whose sources are all remote repositories — those are - // filtered out below and should not appear in the preview manifest, - // since they cannot be edited from the preview/studio UI either. + // Only include non remote collections and collections with at least one local source (remove `info` collection) const localSources = collection.source?.filter(source => !source.repository) ?? [] if (localSources.length === 0) { return acc