@storyblok/api-client 0.1.1 → 0.2.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/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.mts +16 -2
- package/dist/index.mjs +5 -3
- package/dist/index.mjs.map +1 -1
- package/dist/resources/datasource-entries.cjs +6 -2
- package/dist/resources/datasource-entries.cjs.map +1 -1
- package/dist/resources/datasource-entries.mjs +6 -2
- package/dist/resources/datasource-entries.mjs.map +1 -1
- package/dist/resources/datasources.cjs +12 -4
- package/dist/resources/datasources.cjs.map +1 -1
- package/dist/resources/datasources.mjs +12 -4
- package/dist/resources/datasources.mjs.map +1 -1
- package/dist/resources/links.cjs +6 -2
- package/dist/resources/links.cjs.map +1 -1
- package/dist/resources/links.mjs +6 -2
- package/dist/resources/links.mjs.map +1 -1
- package/dist/resources/spaces.cjs +6 -2
- package/dist/resources/spaces.cjs.map +1 -1
- package/dist/resources/spaces.mjs +6 -2
- package/dist/resources/spaces.mjs.map +1 -1
- package/dist/resources/stories.cjs +14 -6
- package/dist/resources/stories.cjs.map +1 -1
- package/dist/resources/stories.mjs +14 -6
- package/dist/resources/stories.mjs.map +1 -1
- package/dist/resources/tags.cjs +6 -2
- package/dist/resources/tags.cjs.map +1 -1
- package/dist/resources/tags.mjs +6 -2
- package/dist/resources/tags.mjs.map +1 -1
- package/dist/types.d.cts +19 -1
- package/dist/types.d.mts +19 -1
- package/dist/utils/cache.cjs +10 -4
- package/dist/utils/cache.cjs.map +1 -1
- package/dist/utils/cache.mjs +10 -4
- package/dist/utils/cache.mjs.map +1 -1
- package/dist/utils/inline-relations.cjs +10 -7
- package/dist/utils/inline-relations.cjs.map +1 -1
- package/dist/utils/inline-relations.mjs +10 -7
- package/dist/utils/inline-relations.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -65,13 +65,16 @@ const resolveRelationMap = async (responseData, requestQuery, { client, throttle
|
|
|
65
65
|
if (relationPaths.length === 0) return null;
|
|
66
66
|
const relationMap = buildRelationMap(responseData.rels);
|
|
67
67
|
if (responseData.rel_uuids?.length) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
const missingUuids = responseData.rel_uuids.filter((uuid) => !relationMap.has(uuid));
|
|
69
|
+
if (missingUuids.length > 0) {
|
|
70
|
+
const fetchedRelations = await fetchMissingRelations({
|
|
71
|
+
client,
|
|
72
|
+
uuids: missingUuids,
|
|
73
|
+
baseQuery: requestQuery,
|
|
74
|
+
throttleManager
|
|
75
|
+
});
|
|
76
|
+
for (const relationStory of fetchedRelations) relationMap.set(relationStory.uuid, relationStory);
|
|
77
|
+
}
|
|
75
78
|
}
|
|
76
79
|
return {
|
|
77
80
|
relationPaths,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline-relations.mjs","names":[],"sources":["../../src/utils/inline-relations.ts"],"sourcesContent":["import type { Client } from '../generated/shared/client';\nimport type { StoryCapi } from '../generated/stories';\nimport type { StoryWithInlinedRelations } from '../resources/stories';\nimport { fetchMissingRelations } from './fetch-rel-uuids';\nimport type { ThrottleManager } from './rate-limit';\n\ntype RelationPath = `${string}.${string}`;\n\ninterface ComponentNode {\n _uid: string;\n component: string;\n [key: string]: unknown;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === 'object' && !Array.isArray(value);\n\nconst isComponentNode = (value: Record<string, unknown>): value is ComponentNode =>\n typeof value.component === 'string' && typeof value._uid === 'string';\n\nconst inlineStoryContentInternal = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n story: TStory,\n relationPaths: ReadonlySet<RelationPath>,\n relationMap: ReadonlyMap<string, TStory>,\n resolved: Map<string, TStory>,\n): TStory => {\n const existingStory = resolved.get(story.uuid);\n if (existingStory) {\n return existingStory;\n }\n\n const clonedStory = structuredClone(story);\n resolved.set(story.uuid, clonedStory);\n // resolveNode returns `unknown` to handle arbitrary JSON trees; shape is preserved at runtime.\n clonedStory.content = resolveNode(clonedStory.content, relationMap, relationPaths, resolved) as StoryCapi['content'];\n return clonedStory;\n};\n\nfunction resolveNode<TStory extends StoryCapi | StoryWithInlinedRelations>(\n value: unknown,\n relationMap: ReadonlyMap<string, TStory>,\n relationPaths: ReadonlySet<RelationPath>,\n resolved: Map<string, TStory>,\n): unknown {\n if (Array.isArray(value)) {\n return value.map(item => resolveNode(item, relationMap, relationPaths, resolved));\n }\n\n if (!isRecord(value)) {\n return value;\n }\n\n if (isComponentNode(value)) {\n for (const [fieldName, fieldValue] of Object.entries(value)) {\n if (fieldName === 'component' || fieldName === '_uid') {\n continue;\n }\n\n const relationPath: RelationPath = `${value.component}.${fieldName}`;\n value[fieldName] = relationPaths.has(relationPath)\n ? resolveFieldValue(fieldValue, relationMap, relationPaths, resolved)\n : resolveNode(fieldValue, relationMap, relationPaths, resolved);\n }\n\n return value;\n }\n\n for (const [fieldName, fieldValue] of Object.entries(value)) {\n value[fieldName] = resolveNode(fieldValue, relationMap, relationPaths, resolved);\n }\n\n return value;\n}\n\nexport const parseResolveRelations = (query: Record<string, unknown>): RelationPath[] => {\n if (typeof query.resolve_relations !== 'string') {\n return [];\n }\n\n return query.resolve_relations\n .split(',')\n .map(path => path.trim())\n .filter((path): path is RelationPath => {\n const [component = '', field = '', ...rest] = path.split('.');\n return component.length > 0 && field.length > 0 && rest.length === 0;\n });\n};\n\nexport const buildRelationMap = (rels: Array<StoryCapi> | undefined): Map<string, StoryCapi> => {\n const relationMap = new Map<string, StoryCapi>();\n\n for (const story of rels ?? []) {\n relationMap.set(story.uuid, story);\n }\n\n return relationMap;\n};\n\nfunction resolveFieldValue<TStory extends StoryCapi | StoryWithInlinedRelations>(\n value: unknown,\n relationMap: ReadonlyMap<string, TStory>,\n relationPaths: ReadonlySet<RelationPath>,\n resolved: Map<string, TStory>,\n): unknown {\n if (typeof value === 'string') {\n const relatedStory = relationMap.get(value);\n if (!relatedStory) {\n return value;\n }\n\n return inlineStoryContentInternal(relatedStory, relationPaths, relationMap, resolved);\n }\n\n if (Array.isArray(value)) {\n return value.map(item => resolveFieldValue(item, relationMap, relationPaths, resolved));\n }\n\n return resolveNode(value, relationMap, relationPaths, resolved);\n}\n\nexport const inlineStoryContent = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n story: TStory,\n relationPaths: RelationPath[],\n relationMap: ReadonlyMap<string, TStory>,\n): TStory => {\n const normalizedPaths = new Set(relationPaths);\n const resolved = new Map<string, TStory>();\n return inlineStoryContentInternal(story, normalizedPaths, relationMap, resolved);\n};\n\nexport const inlineStoriesContent = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n stories: Array<TStory>,\n relationPaths: RelationPath[],\n relationMap: ReadonlyMap<string, TStory>,\n): Array<TStory> => {\n const normalizedPaths = new Set(relationPaths);\n const resolved = new Map<string, TStory>();\n return stories.map(story => inlineStoryContentInternal(story, normalizedPaths, relationMap, resolved));\n};\n\ninterface ResolveRelationMapOptions {\n client: Client;\n throttleManager: ThrottleManager;\n}\n\nexport interface ResolvedRelations {\n relationPaths: RelationPath[];\n relationMap: Map<string, StoryCapi>;\n}\n\n/**\n * Parses relation paths from the request query, builds a relation map from the\n * response's `rels`, and fetches any additional relations referenced by `rel_uuids`.\n *\n * Returns `null` when there is nothing to inline (no `resolve_relations` in the query).\n */\nexport const resolveRelationMap = async (\n responseData: { rels?: StoryCapi[]; rel_uuids?: string[] },\n requestQuery: Record<string, unknown>,\n { client, throttleManager }: ResolveRelationMapOptions,\n): Promise<ResolvedRelations | null> => {\n const relationPaths = parseResolveRelations(requestQuery);\n if (relationPaths.length === 0) {\n return null;\n }\n\n const relationMap = buildRelationMap(responseData.rels);\n if (responseData.rel_uuids?.length) {\n const fetchedRelations = await fetchMissingRelations({\n
|
|
1
|
+
{"version":3,"file":"inline-relations.mjs","names":[],"sources":["../../src/utils/inline-relations.ts"],"sourcesContent":["import type { Client } from '../generated/shared/client';\nimport type { StoryCapi } from '../generated/stories';\nimport type { StoryWithInlinedRelations } from '../resources/stories';\nimport { fetchMissingRelations } from './fetch-rel-uuids';\nimport type { ThrottleManager } from './rate-limit';\n\ntype RelationPath = `${string}.${string}`;\n\ninterface ComponentNode {\n _uid: string;\n component: string;\n [key: string]: unknown;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === 'object' && !Array.isArray(value);\n\nconst isComponentNode = (value: Record<string, unknown>): value is ComponentNode =>\n typeof value.component === 'string' && typeof value._uid === 'string';\n\nconst inlineStoryContentInternal = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n story: TStory,\n relationPaths: ReadonlySet<RelationPath>,\n relationMap: ReadonlyMap<string, TStory>,\n resolved: Map<string, TStory>,\n): TStory => {\n const existingStory = resolved.get(story.uuid);\n if (existingStory) {\n return existingStory;\n }\n\n const clonedStory = structuredClone(story);\n resolved.set(story.uuid, clonedStory);\n // resolveNode returns `unknown` to handle arbitrary JSON trees; shape is preserved at runtime.\n clonedStory.content = resolveNode(clonedStory.content, relationMap, relationPaths, resolved) as StoryCapi['content'];\n return clonedStory;\n};\n\nfunction resolveNode<TStory extends StoryCapi | StoryWithInlinedRelations>(\n value: unknown,\n relationMap: ReadonlyMap<string, TStory>,\n relationPaths: ReadonlySet<RelationPath>,\n resolved: Map<string, TStory>,\n): unknown {\n if (Array.isArray(value)) {\n return value.map(item => resolveNode(item, relationMap, relationPaths, resolved));\n }\n\n if (!isRecord(value)) {\n return value;\n }\n\n if (isComponentNode(value)) {\n for (const [fieldName, fieldValue] of Object.entries(value)) {\n if (fieldName === 'component' || fieldName === '_uid') {\n continue;\n }\n\n const relationPath: RelationPath = `${value.component}.${fieldName}`;\n value[fieldName] = relationPaths.has(relationPath)\n ? resolveFieldValue(fieldValue, relationMap, relationPaths, resolved)\n : resolveNode(fieldValue, relationMap, relationPaths, resolved);\n }\n\n return value;\n }\n\n for (const [fieldName, fieldValue] of Object.entries(value)) {\n value[fieldName] = resolveNode(fieldValue, relationMap, relationPaths, resolved);\n }\n\n return value;\n}\n\nexport const parseResolveRelations = (query: Record<string, unknown>): RelationPath[] => {\n if (typeof query.resolve_relations !== 'string') {\n return [];\n }\n\n return query.resolve_relations\n .split(',')\n .map(path => path.trim())\n .filter((path): path is RelationPath => {\n const [component = '', field = '', ...rest] = path.split('.');\n return component.length > 0 && field.length > 0 && rest.length === 0;\n });\n};\n\nexport const buildRelationMap = (rels: Array<StoryCapi> | undefined): Map<string, StoryCapi> => {\n const relationMap = new Map<string, StoryCapi>();\n\n for (const story of rels ?? []) {\n relationMap.set(story.uuid, story);\n }\n\n return relationMap;\n};\n\nfunction resolveFieldValue<TStory extends StoryCapi | StoryWithInlinedRelations>(\n value: unknown,\n relationMap: ReadonlyMap<string, TStory>,\n relationPaths: ReadonlySet<RelationPath>,\n resolved: Map<string, TStory>,\n): unknown {\n if (typeof value === 'string') {\n const relatedStory = relationMap.get(value);\n if (!relatedStory) {\n return value;\n }\n\n return inlineStoryContentInternal(relatedStory, relationPaths, relationMap, resolved);\n }\n\n if (Array.isArray(value)) {\n return value.map(item => resolveFieldValue(item, relationMap, relationPaths, resolved));\n }\n\n return resolveNode(value, relationMap, relationPaths, resolved);\n}\n\nexport const inlineStoryContent = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n story: TStory,\n relationPaths: RelationPath[],\n relationMap: ReadonlyMap<string, TStory>,\n): TStory => {\n const normalizedPaths = new Set(relationPaths);\n const resolved = new Map<string, TStory>();\n return inlineStoryContentInternal(story, normalizedPaths, relationMap, resolved);\n};\n\nexport const inlineStoriesContent = <TStory extends StoryCapi | StoryWithInlinedRelations>(\n stories: Array<TStory>,\n relationPaths: RelationPath[],\n relationMap: ReadonlyMap<string, TStory>,\n): Array<TStory> => {\n const normalizedPaths = new Set(relationPaths);\n const resolved = new Map<string, TStory>();\n return stories.map(story => inlineStoryContentInternal(story, normalizedPaths, relationMap, resolved));\n};\n\ninterface ResolveRelationMapOptions {\n client: Client;\n throttleManager: ThrottleManager;\n}\n\nexport interface ResolvedRelations {\n relationPaths: RelationPath[];\n relationMap: Map<string, StoryCapi>;\n}\n\n/**\n * Parses relation paths from the request query, builds a relation map from the\n * response's `rels`, and fetches any additional relations referenced by `rel_uuids`.\n *\n * Returns `null` when there is nothing to inline (no `resolve_relations` in the query).\n */\nexport const resolveRelationMap = async (\n responseData: { rels?: StoryCapi[]; rel_uuids?: string[] },\n requestQuery: Record<string, unknown>,\n { client, throttleManager }: ResolveRelationMapOptions,\n): Promise<ResolvedRelations | null> => {\n const relationPaths = parseResolveRelations(requestQuery);\n if (relationPaths.length === 0) {\n return null;\n }\n\n const relationMap = buildRelationMap(responseData.rels);\n if (responseData.rel_uuids?.length) {\n const missingUuids = responseData.rel_uuids.filter(uuid => !relationMap.has(uuid));\n if (missingUuids.length > 0) {\n const fetchedRelations = await fetchMissingRelations({\n client,\n uuids: missingUuids,\n baseQuery: requestQuery,\n throttleManager,\n });\n for (const relationStory of fetchedRelations) {\n relationMap.set(relationStory.uuid, relationStory);\n }\n }\n }\n\n return { relationPaths, relationMap };\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyblok/api-client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Storyblok Content Delivery API Client",
|
|
7
7
|
"author": "",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"tsdown": "^0.20.3",
|
|
44
44
|
"tsx": "^4.21.0",
|
|
45
45
|
"vitest": "^4.0.18",
|
|
46
|
-
"@storyblok/
|
|
47
|
-
"@storyblok/
|
|
46
|
+
"@storyblok/eslint-config": "0.5.0",
|
|
47
|
+
"@storyblok/openapi": "1.2.0"
|
|
48
48
|
},
|
|
49
49
|
"nx": {
|
|
50
50
|
"targets": {
|