astro-loader-pocketbase 3.1.1 → 3.1.2-next.2
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.d.mts +276 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +973 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +26 -14
- package/src/index.ts +0 -24
- package/src/loader/cleanup-entries.ts +0 -137
- package/src/loader/fetch-collection.ts +0 -177
- package/src/loader/fetch-entry.ts +0 -83
- package/src/loader/handle-realtime-updates.ts +0 -56
- package/src/loader/live-collection-loader.ts +0 -51
- package/src/loader/live-entry-loader.ts +0 -38
- package/src/loader/load-entries.ts +0 -52
- package/src/loader/loader.ts +0 -77
- package/src/loader/parse-entry.ts +0 -90
- package/src/loader/parse-live-entry.ts +0 -66
- package/src/pocketbase-loader.ts +0 -98
- package/src/schema/generate-schema.ts +0 -200
- package/src/schema/generate-type.ts +0 -23
- package/src/schema/get-remote-schema.ts +0 -43
- package/src/schema/parse-schema.ts +0 -170
- package/src/schema/read-local-schema.ts +0 -46
- package/src/schema/transform-files.ts +0 -67
- package/src/tsconfig.json +0 -7
- package/src/types/errors.ts +0 -19
- package/src/types/pocketbase-api-response.type.ts +0 -40
- package/src/types/pocketbase-entry.type.ts +0 -24
- package/src/types/pocketbase-live-loader-filter.type.ts +0 -55
- package/src/types/pocketbase-loader-options.type.ts +0 -146
- package/src/types/pocketbase-schema.type.ts +0 -101
- package/src/utils/combine-fields-for-request.ts +0 -55
- package/src/utils/create-token-promise.ts +0 -25
- package/src/utils/extract-field-names.ts +0 -15
- package/src/utils/format-fields.ts +0 -66
- package/src/utils/get-superuser-token.ts +0 -76
- package/src/utils/is-realtime-data.ts +0 -34
- package/src/utils/should-refresh.ts +0 -37
- package/src/utils/slugify.ts +0 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["packageJson.version"],"sources":["../src/types/errors.ts","../src/types/pocketbase-entry.type.ts","../src/types/pocketbase-api-response.type.ts","../src/utils/combine-fields-for-request.ts","../src/utils/format-fields.ts","../src/loader/fetch-collection.ts","../src/loader/parse-live-entry.ts","../src/loader/live-collection-loader.ts","../src/loader/fetch-entry.ts","../src/loader/live-entry-loader.ts","../package.json","../src/utils/should-refresh.ts","../src/loader/cleanup-entries.ts","../src/utils/is-realtime-data.ts","../src/utils/slugify.ts","../src/loader/parse-entry.ts","../src/loader/handle-realtime-updates.ts","../src/loader/load-entries.ts","../src/loader/loader.ts","../src/utils/extract-field-names.ts","../src/types/pocketbase-schema.type.ts","../src/schema/get-remote-schema.ts","../src/schema/parse-schema.ts","../src/schema/read-local-schema.ts","../src/schema/transform-files.ts","../src/schema/generate-schema.ts","../src/schema/generate-type.ts","../src/utils/get-superuser-token.ts","../src/utils/create-token-promise.ts","../src/pocketbase-loader.ts"],"sourcesContent":["import { LiveCollectionError } from \"astro/content/runtime\";\n\n/**\n * Error thrown when there is an authentication issue with PocketBase.\n */\nexport class PocketBaseAuthenticationError extends LiveCollectionError {\n constructor(collection: string, message: string) {\n super(collection, message);\n this.name = \"PocketBaseAuthenticationError\";\n }\n\n static is(error: unknown): error is PocketBaseAuthenticationError {\n // This is similar to the original implementation in Astro itself.\n return (\n // oxlint-disable-next-line no-unsafe-type-assertion\n !!error && (error as Error).name === \"PocketBaseAuthenticationError\"\n );\n }\n}\n","import { z } from \"astro/zod\";\n\n/**\n * Schema for a PocketBase entry.\n */\nexport const pocketBaseEntry = z.looseObject({\n /**\n * ID of the entry.\n */\n id: z.string(),\n /**\n * ID of the collection the entry belongs to.\n */\n collectionId: z.string(),\n /**\n * Name of the collection the entry belongs to.\n */\n collectionName: z.string()\n});\n\n/**\n * Type for a PocketBase entry.\n */\nexport type PocketBaseEntry = z.infer<typeof pocketBaseEntry>;\n","import { z } from \"astro/zod\";\nimport { pocketBaseEntry } from \"./pocketbase-entry.type\";\n\n/**\n * The schema for a PocketBase error response.\n */\nexport const pocketBaseErrorResponse = z.object({\n /**\n * The error message returned by PocketBase.\n */\n message: z.string()\n});\n\n/**\n * The schema for a PocketBase list response.\n */\nexport const pocketBaseListResponse = z.object({\n /**\n * Current page number.\n */\n page: z.number(),\n /**\n * Total number of pages available.\n */\n totalPages: z.number(),\n /**\n * Array of items in the current page.\n */\n items: z.array(pocketBaseEntry)\n});\n\n/**\n * The schema for a PocketBase login response.\n */\nexport const pocketBaseLoginResponse = z.object({\n /**\n * The authentication token returned by PocketBase.\n */\n token: z.string()\n});\n","import type {\n PocketBaseLoaderBaseOptions,\n PocketBaseLoaderOptions\n} from \"../types/pocketbase-loader-options.type\";\n\n/**\n * Combine basic, special, and user-specified fields for PocketBase API requests.\n * This utility ensures that required system fields are always included in API requests.\n *\n * @param userFields Array of fields specified by the user, or undefined for all fields\n * @param options PocketBase loader options containing custom field configurations\n * @returns Combined array of fields to include in the API request, or undefined for all fields\n */\nexport function combineFieldsForRequest(\n userFields: Array<string> | undefined,\n options: Pick<PocketBaseLoaderBaseOptions, \"updatedField\" | \"contentFields\"> &\n Pick<PocketBaseLoaderOptions, \"idField\">\n): Array<string> | undefined {\n // If no fields specified, return undefined to get all fields\n if (!userFields) {\n return undefined;\n }\n\n // Basic fields that are always required by the loader\n const basicFields = [\"id\", \"collectionId\", \"collectionName\"];\n\n // Special fields that are configured in options\n const specialFields: Array<string> = [];\n\n // Add custom id field if specified\n if (options.idField && options.idField !== \"id\") {\n specialFields.push(options.idField);\n }\n\n // Add updated field if specified\n if (options.updatedField) {\n specialFields.push(options.updatedField);\n }\n\n // Add content fields if specified\n if (options.contentFields) {\n if (Array.isArray(options.contentFields)) {\n specialFields.push(...options.contentFields);\n } else {\n specialFields.push(options.contentFields);\n }\n }\n\n // Combine all field sets, removing duplicates\n const allFields = [\n ...new Set([...basicFields, ...specialFields, ...userFields])\n ];\n\n return allFields;\n}\n","import type { PocketBaseLoaderBaseOptions } from \"../types/pocketbase-loader-options.type\";\n\n/**\n * Format fields option into an array and validate for expand usage.\n * Handles wildcard \"*\" and preserves excerpt field modifiers.\n *\n * @param fields The fields option (string or array)\n * @returns Formatted fields array, or undefined if no fields specified or \"*\" wildcard is used\n */\nexport function formatFields(\n fields: PocketBaseLoaderBaseOptions[\"fields\"]\n): Array<string> | undefined {\n if (!fields || fields.length === 0) {\n return undefined;\n }\n\n let fieldList: Array<string>;\n if (Array.isArray(fields)) {\n fieldList = fields.map((f) => f.trim());\n } else {\n // Split carefully, respecting parentheses in excerpt syntax\n fieldList = splitFieldsString(fields).map((f) => f.trim());\n }\n\n // Warn if expand is used since it's not currently supported\n const hasExpand = fieldList.some((field) => field.includes(\"expand\"));\n if (hasExpand) {\n console.warn(\n 'The \"expand\" parameter is not currently supported by astro-loader-pocketbase and will be filtered out.'\n );\n fieldList = fieldList.filter((field) => !field.includes(\"expand\"));\n }\n\n // Check for \"*\" wildcard - if found anywhere, include all fields\n const hasWildcard = fieldList.some((field) => field === \"*\");\n if (hasWildcard) {\n return undefined;\n }\n\n return fieldList;\n}\n\n/**\n * Splits the fields string at `,` but respects the `:excerpt(number, boolean)` option\n */\nfunction splitFieldsString(fieldsString: string): Array<string> {\n // First, split by comma\n const initialSplit = fieldsString.split(\",\");\n\n const fields: Array<string> = [];\n for (let i = 0; i < initialSplit.length; i++) {\n const part = initialSplit.at(i);\n if (!part) {\n continue;\n }\n\n if (part.includes(\"(\") && !part.includes(\")\")) {\n fields.push(`${part},${initialSplit[++i]}`);\n continue;\n }\n\n fields.push(part);\n }\n\n return fields;\n}\n","import {\n LiveCollectionError,\n LiveEntryNotFoundError\n} from \"astro/content/runtime\";\nimport { PocketBaseAuthenticationError } from \"../types/errors\";\nimport {\n pocketBaseErrorResponse,\n pocketBaseListResponse\n} from \"../types/pocketbase-api-response.type\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLiveLoaderCollectionFilter } from \"../types/pocketbase-live-loader-filter.type\";\nimport type { PocketBaseLoaderBaseOptions } from \"../types/pocketbase-loader-options.type\";\nimport { combineFieldsForRequest } from \"../utils/combine-fields-for-request\";\nimport { formatFields } from \"../utils/format-fields\";\n\n/**\n * Provides utilities to fetch entries from a PocketBase collection, supporting filtering and pagination.\n */\nexport type CollectionFilter = {\n /**\n * Date string to only fetch entries that have been modified since this date.\n * If not provided, all entries will be fetched.\n */\n lastModified?: string;\n} & PocketBaseLiveLoaderCollectionFilter;\n\n/**\n * Fetches entries from a PocketBase collection, optionally filtering by modification date and supporting pagination.\n */\nexport async function fetchCollection<TEntry extends PocketBaseEntry>(\n options: PocketBaseLoaderBaseOptions,\n chunkLoaded: (entries: Array<TEntry>) => Promise<void>,\n token: string | undefined,\n collectionFilter: CollectionFilter | undefined\n): Promise<void> {\n // Build the URL for the collections endpoint\n const collectionUrl = new URL(\n `api/collections/${options.collectionName}/records`,\n options.url\n );\n\n // Create the headers for the request to append the token (if available)\n const collectionHeaders = new Headers();\n if (token) {\n collectionHeaders.set(\"Authorization\", token);\n }\n\n // Cache fields computation outside the loop\n const fieldsArray = formatFields(options.fields);\n const combinedFields = combineFieldsForRequest(fieldsArray, options);\n\n // Prepare pagination variables\n let page = 0;\n let totalPages = 0;\n\n // Fetch all (modified) entries\n do {\n const searchParams = buildSearchParams(options, combinedFields, {\n ...collectionFilter,\n page: collectionFilter?.page ?? ++page,\n perPage: collectionFilter?.perPage ?? 100\n });\n\n // Apply search parameters to URL\n collectionUrl.search = searchParams.toString();\n\n // Fetch entries from the collection\n const collectionRequest = await fetch(collectionUrl.href, {\n headers: collectionHeaders\n });\n\n // If the request was not successful, print the error message and return\n if (!collectionRequest.ok) {\n // If the collection is locked, an superuser token is required\n if (collectionRequest.status === 403) {\n if (\n options.superuserCredentials &&\n \"impersonateToken\" in options.superuserCredentials\n ) {\n throw new PocketBaseAuthenticationError(\n options.collectionName,\n \"The given impersonate token is not valid.\"\n );\n } else {\n throw new PocketBaseAuthenticationError(\n options.collectionName,\n \"The collection is not accessible without superuser rights. Please provide superuser credentials in the config.\"\n );\n }\n }\n\n if (collectionRequest.status === 404) {\n throw new LiveEntryNotFoundError(options.collectionName, {\n ...collectionFilter\n });\n }\n\n // Get the reason for the error\n const errorResponse = pocketBaseErrorResponse.parse(\n await collectionRequest.json()\n );\n throw new LiveCollectionError(\n options.collectionName,\n errorResponse.message\n );\n }\n\n // Get the data from the response\n const response = pocketBaseListResponse.parse(\n await collectionRequest.json()\n );\n\n // Return current chunk\n // oxlint-disable-next-line no-unsafe-type-assertion\n await chunkLoaded(response.items as Array<TEntry>);\n\n // Update the page and total pages\n page = response.page;\n totalPages = response.totalPages;\n } while (!collectionFilter?.perPage && page < totalPages);\n}\n\n/**\n * Build search parameters for the PocketBase collection request.\n */\nfunction buildSearchParams(\n loaderOptions: PocketBaseLoaderBaseOptions,\n combinedFields: Array<string> | undefined,\n collectionFilter: CollectionFilter\n): URLSearchParams {\n const searchParams = new URLSearchParams();\n\n if (collectionFilter.page) {\n searchParams.set(\"page\", `${collectionFilter.page}`);\n }\n\n if (collectionFilter.perPage) {\n searchParams.set(\"perPage\", `${collectionFilter.perPage}`);\n }\n\n const filters = [];\n\n // If `lastModified` is set, only fetch entries that have been modified since the last fetch\n // Sort by the updated field and id\n if (collectionFilter.lastModified && loaderOptions.updatedField) {\n filters.push(\n `(${loaderOptions.updatedField}>\"${collectionFilter.lastModified}\")`\n );\n searchParams.set(\"sort\", `-${loaderOptions.updatedField},id`);\n }\n\n // Add filter from the loader options\n if (loaderOptions.filter) {\n filters.push(`(${loaderOptions.filter})`);\n }\n\n // Add additional filter from the collection filter\n if (collectionFilter.filter) {\n filters.push(`(${collectionFilter.filter})`);\n }\n\n // Add filters to search parameters\n if (filters.length > 0) {\n searchParams.set(\"filter\", filters.join(\"&&\"));\n }\n\n if (collectionFilter.sort) {\n searchParams.set(\"sort\", collectionFilter.sort);\n }\n\n // Add fields parameter if specified\n if (combinedFields) {\n searchParams.set(\"fields\", combinedFields.join(\",\"));\n }\n\n return searchParams;\n}\n","import type { LiveDataEntry } from \"astro\";\nimport { LiveCollectionValidationError } from \"astro/content/runtime\";\nimport { z } from \"astro/zod\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLiveLoaderOptions } from \"../types/pocketbase-loader-options.type\";\n\n/**\n * Converts a PocketBase entry into a LiveDataEntry for Astro, extracting content and cache metadata.\n */\nexport function parseLiveEntry<TEntry extends PocketBaseEntry>(\n entry: TEntry,\n options: PocketBaseLiveLoaderOptions\n): LiveDataEntry<TEntry> {\n // Build a cache tag\n const tag = `${options.collectionName}-${entry.id}`;\n\n let lastModified: Date | undefined = undefined;\n // If an updated field is provided and the entry has a valid date value,\n // use it as the last modified date cache hint\n if (options.updatedField && entry[options.updatedField]) {\n const value = `${entry[options.updatedField]}`;\n const date = z.coerce.date().safeParse(value);\n if (!date.success) {\n throw new LiveCollectionValidationError(\n options.collectionName,\n entry.id,\n date.error\n );\n }\n lastModified = date.data;\n }\n\n if (!options.contentFields) {\n return {\n id: entry.id,\n data: entry,\n cacheHint: {\n tags: [tag],\n lastModified\n }\n };\n }\n\n let content: string;\n if (typeof options.contentFields === \"string\") {\n // If a single content field is provided, use it directly\n content = `${entry[options.contentFields]}`;\n } else {\n // If multiple content fields are provided, concatenate them with `<section>` tags\n content = options.contentFields\n .map((field) => `<section id=\"${field}\">${entry[field]}</section>`)\n .join(\"\");\n }\n\n return {\n id: entry.id,\n data: entry,\n rendered: {\n html: content\n },\n cacheHint: {\n tags: [tag],\n lastModified\n }\n };\n}\n","import type { LiveDataCollection, LiveDataEntry } from \"astro\";\nimport { LiveCollectionError } from \"astro/content/runtime\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLiveLoaderCollectionFilter } from \"../types/pocketbase-live-loader-filter.type\";\nimport type { PocketBaseLiveLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { fetchCollection } from \"./fetch-collection\";\nimport { parseLiveEntry } from \"./parse-live-entry\";\n\n/**\n * Loads and parses a PocketBase collection for live data, returning entries or an error.\n */\nexport async function liveCollectionLoader<TEntry extends PocketBaseEntry>(\n collectionFilter: PocketBaseLiveLoaderCollectionFilter | undefined,\n options: PocketBaseLiveLoaderOptions,\n token: string | undefined\n): Promise<LiveDataCollection<TEntry> | { error: LiveCollectionError }> {\n const entries: Array<LiveDataEntry<TEntry>> = [];\n\n try {\n await fetchCollection<TEntry>(\n options,\n async (chunk) => {\n entries.push(...chunk.map((entry) => parseLiveEntry(entry, options)));\n },\n token,\n collectionFilter\n );\n } catch (error) {\n if (error instanceof LiveCollectionError) {\n return { error };\n }\n\n if (error instanceof Error) {\n return {\n error: new LiveCollectionError(\n options.collectionName,\n error.message,\n error\n )\n };\n }\n\n return {\n error: new LiveCollectionError(options.collectionName, String(error))\n };\n }\n\n return {\n entries\n };\n}\n","import {\n LiveCollectionError,\n LiveEntryNotFoundError\n} from \"astro/content/runtime\";\nimport { PocketBaseAuthenticationError } from \"../types/errors\";\nimport { pocketBaseErrorResponse } from \"../types/pocketbase-api-response.type\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport { pocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLiveLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { combineFieldsForRequest } from \"../utils/combine-fields-for-request\";\nimport { formatFields } from \"../utils/format-fields\";\n\n/**\n * Retrieves a specific entry from a PocketBase collection using its ID and loader options.\n */\nexport async function fetchEntry<TEntry extends PocketBaseEntry>(\n id: string,\n options: PocketBaseLiveLoaderOptions,\n token: string | undefined\n): Promise<TEntry> {\n // Build the URL for the entry endpoint\n const entryUrl = new URL(\n `api/collections/${options.collectionName}/records/${id}`,\n options.url\n );\n\n // Add fields parameter if specified\n const fieldsArray = formatFields(options.fields);\n const combinedFields = combineFieldsForRequest(fieldsArray, options);\n if (combinedFields) {\n entryUrl.searchParams.set(\"fields\", combinedFields.join(\",\"));\n }\n\n // Create the headers for the request to append the token (if available)\n const entryHeaders = new Headers();\n if (token) {\n entryHeaders.set(\"Authorization\", token);\n }\n\n // Fetch the entry from the collection\n const entryRequest = await fetch(entryUrl.href, {\n headers: entryHeaders\n });\n\n // If the request was not successful, return an error\n if (!entryRequest.ok) {\n // If the entry is locked, a superuser token is required\n if (entryRequest.status === 403) {\n if (\n options.superuserCredentials &&\n \"impersonateToken\" in options.superuserCredentials\n ) {\n throw new PocketBaseAuthenticationError(\n options.collectionName,\n \"The given impersonate token is not valid.\"\n );\n } else {\n throw new PocketBaseAuthenticationError(\n options.collectionName,\n \"The entry is not accessible without superuser rights. Please provide superuser credentials in the config.\"\n );\n }\n }\n\n if (entryRequest.status === 404) {\n throw new LiveEntryNotFoundError(options.collectionName, id);\n }\n\n // Get the reason for the error\n const errorResponse = pocketBaseErrorResponse.parse(\n await entryRequest.json()\n );\n throw new LiveCollectionError(\n options.collectionName,\n errorResponse.message\n );\n }\n\n // Get the data from the response\n const response = pocketBaseEntry.parse(await entryRequest.json());\n // oxlint-disable-next-line no-unsafe-type-assertion\n return response as TEntry;\n}\n","import type { LiveDataEntry } from \"astro\";\nimport { LiveCollectionError } from \"astro/content/runtime\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLiveLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { fetchEntry } from \"./fetch-entry\";\nimport { parseLiveEntry } from \"./parse-live-entry\";\n\n/**\n * Loads and parses a single PocketBase entry for live data, returning the entry or an error.\n */\nexport async function liveEntryLoader<TEntry extends PocketBaseEntry>(\n id: string,\n options: PocketBaseLiveLoaderOptions,\n token: string | undefined\n): Promise<LiveDataEntry<TEntry> | { error: LiveCollectionError }> {\n try {\n const entry = await fetchEntry<TEntry>(id, options, token);\n return parseLiveEntry(entry, options);\n } catch (error) {\n if (error instanceof LiveCollectionError) {\n return { error };\n }\n\n if (error instanceof Error) {\n return {\n error: new LiveCollectionError(\n options.collectionName,\n error.message,\n error\n )\n };\n }\n\n return {\n error: new LiveCollectionError(options.collectionName, String(error))\n };\n }\n}\n","","import type { LoaderContext } from \"astro/loaders\";\n\n/**\n * Checks if the collection should be refreshed.\n */\nexport function shouldRefresh(\n context: LoaderContext[\"refreshContextData\"],\n collectionName: string\n): \"refresh\" | \"skip\" | \"force\" {\n // Check if the refresh was triggered by the `astro-integration-pocketbase`\n // and the correct metadata is provided.\n if (context?.source !== \"astro-integration-pocketbase\") {\n return \"refresh\";\n }\n\n // Check if all collections should be refreshed.\n if (context.force) {\n return \"force\";\n }\n\n if (!context.collection) {\n return \"refresh\";\n }\n\n // Check if the collection name matches the current collection.\n if (typeof context.collection === \"string\") {\n return context.collection === collectionName ? \"refresh\" : \"skip\";\n }\n\n // Check if the collection is included in the list of collections.\n if (Array.isArray(context.collection)) {\n return context.collection.includes(collectionName) ? \"refresh\" : \"skip\";\n }\n\n // Should not happen but return true to be safe.\n return \"refresh\";\n}\n","import type { LoaderContext } from \"astro/loaders\";\nimport { z } from \"astro/zod\";\nimport {\n pocketBaseErrorResponse,\n pocketBaseListResponse\n} from \"../types/pocketbase-api-response.type\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport { pocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLoaderBaseOptions } from \"../types/pocketbase-loader-options.type\";\n\n/**\n * Cleanup entries that are no longer in the collection.\n *\n * @param options Options for the loader.\n * @param context Context of the loader.\n * @param superuserToken Superuser token to access all resources.\n */\nexport async function cleanupEntries(\n options: PocketBaseLoaderBaseOptions,\n context: LoaderContext,\n superuserToken: string | undefined\n): Promise<void> {\n // Build the URL for the collections endpoint\n const collectionUrl = new URL(\n `api/collections/${options.collectionName}/records`,\n options.url\n ).href;\n\n // Create the headers for the request to append the superuser token (if available)\n const collectionHeaders = new Headers();\n if (superuserToken) {\n collectionHeaders.set(\"Authorization\", superuserToken);\n }\n\n // Prepare pagination variables\n let page = 0;\n let totalPages = 0;\n const entries = new Set<string>();\n\n // Fetch all ids of the collection\n do {\n // Build search parameters\n const searchParams = new URLSearchParams({\n page: `${++page}`,\n perPage: \"1000\",\n fields: \"id\"\n });\n\n if (options.filter) {\n // If a filter is set, add it to the search parameters\n searchParams.set(\"filter\", `(${options.filter})`);\n }\n\n // Fetch ids from the collection\n const collectionRequest = await fetch(\n `${collectionUrl}?${searchParams.toString()}`,\n {\n headers: collectionHeaders\n }\n );\n\n // If the request was not successful, print the error message and return\n if (!collectionRequest.ok) {\n // If the collection is locked, an superuser token is required\n if (collectionRequest.status === 403) {\n if (\n options.superuserCredentials &&\n \"impersonateToken\" in options.superuserCredentials\n ) {\n context.logger.error(\"The given impersonate token is not valid.\");\n } else {\n context.logger.error(\n \"The collection is not accessible without superuser rights. Please provide superuser credentials in the config.\"\n );\n }\n } else {\n const errorResponse = pocketBaseErrorResponse.parse(\n await collectionRequest.json()\n );\n const errorMessage = `Fetching ids failed with status code ${collectionRequest.status}.\\nReason: ${errorResponse.message}`;\n context.logger.error(errorMessage);\n }\n\n // Remove all entries from the store\n context.logger.info(`Removing all entries from the store.`);\n context.store.clear();\n return;\n }\n\n // Get the data from the response\n const response = cleanUpEntriesResponse.parse(\n await collectionRequest.json()\n );\n\n // Add the ids to the set\n for (const item of response.items) {\n entries.add(item.id);\n }\n\n // Update the page and total pages\n page = response.page;\n totalPages = response.totalPages;\n } while (page < totalPages);\n\n let cleanedUp = 0;\n\n // Create a mapping from PocketBase IDs to store keys for proper cleanup\n const storedIds = new Map<string, string>(\n context.store\n .values()\n // oxlint-disable-next-line no-unsafe-type-assertion\n .map((entry) => [(entry.data as PocketBaseEntry).id, entry.id])\n );\n\n // Check which PocketBase IDs are missing from the server response\n for (const [pocketbaseId, storeKey] of storedIds.entries()) {\n // If the PocketBase ID is not in the entries set, remove the entry from the store\n if (!entries.has(pocketbaseId)) {\n context.store.delete(storeKey);\n cleanedUp++;\n }\n }\n\n if (cleanedUp > 0) {\n // Log the number of cleaned up entries\n context.logger.info(`Cleaned up ${cleanedUp} old entries.`);\n }\n}\n\n/**\n * The response schema for cleaning up entries.\n */\nconst cleanUpEntriesResponse = pocketBaseListResponse\n .omit({ items: true })\n .extend({\n items: z.array(pocketBaseEntry.pick({ id: true }))\n });\n","import { z } from \"astro/zod\";\n\n/**\n * Schema for realtime data received from PocketBase.\n */\nconst realtimeDataSchema = z.object({\n action: z.union([\n z.literal(\"create\"),\n z.literal(\"update\"),\n z.literal(\"delete\")\n ]),\n record: z.object({\n id: z.string(),\n collectionName: z.string(),\n collectionId: z.string()\n })\n});\n\n/**\n * Type for realtime data received from PocketBase.\n */\nexport type RealtimeData = z.infer<typeof realtimeDataSchema>;\n\n/**\n * Checks if the given data is realtime data received from PocketBase.\n */\nexport function isRealtimeData(data: unknown): data is RealtimeData {\n try {\n realtimeDataSchema.parse(data);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Convert a string to a slug.\n *\n * Example:\n * ```ts\n * slugify(\"Hello World!\"); // hello-world\n * ```\n */\nexport function slugify(input: string): string {\n return input\n .toLowerCase()\n .replaceAll(/\\s+/g, \"-\") // Replace spaces with -\n .replaceAll(\"ä\", \"ae\") // Replace umlauts\n .replaceAll(\"ö\", \"oe\")\n .replaceAll(\"ü\", \"ue\")\n .replaceAll(\"ß\", \"ss\")\n .replaceAll(/[^\\w-]+/g, \"\") // Remove all non-word chars\n .replaceAll(/--+/g, \"-\") // Replace multiple - with single -\n .replace(/^-+/, \"\") // Trim - from start of text\n .replace(/-+$/, \"\"); // Trim - from end of text\n}\n","import type { LoaderContext } from \"astro/loaders\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { slugify } from \"../utils/slugify\";\n\n/**\n * Parse an entry from PocketBase to match the schema and store it in the store.\n *\n * @param entry Entry to parse.\n * @param context Context of the loader.\n * @param idField Field to use as id for the entry.\n * If not provided, the id of the entry will be used.\n * @param contentFields Field(s) to use as content for the entry.\n * If multiple fields are used, they will be concatenated and wrapped in `<section>` elements.\n */\nexport async function parseEntry(\n entry: PocketBaseEntry,\n { generateDigest, parseData, store, logger }: LoaderContext,\n { idField, contentFields, updatedField }: PocketBaseLoaderOptions\n): Promise<void> {\n let id = entry.id;\n if (idField) {\n // Get the custom ID of the entry if it exists\n const customEntryId = entry[idField];\n\n if (!customEntryId) {\n logger.warn(\n `The entry \"${id}\" does not have a value for field ${idField}. Using the default ID instead.`\n );\n } else {\n id = slugify(`${customEntryId}`);\n }\n }\n\n const oldEntry = store.get(id);\n if (oldEntry && oldEntry.data.id !== entry.id) {\n logger.warn(\n `The entry \"${entry.id}\" seems to be a duplicate of \"${oldEntry.data.id}\". Please make sure to use unique IDs in the column \"${idField}\".`\n );\n }\n\n // Parse the data to match the schema\n // This will throw an error if the data does not match the schema\n const data = await parseData({\n id,\n data: entry\n });\n\n // Get the updated date of the entry\n let updated: string | undefined;\n if (updatedField) {\n updated = `${entry[updatedField]}`;\n }\n\n // Generate a digest for the entry\n // If no updated date is available, the digest will be generated from the whole entry\n const digest = generateDigest(updated ?? entry);\n\n if (!contentFields) {\n // Store the entry\n store.set({\n id,\n data,\n digest\n });\n return;\n }\n\n // Generate the content for the entry\n let content: string;\n if (typeof contentFields === \"string\") {\n // Only one field is used as content\n content = `${entry[contentFields]}`;\n } else {\n // Multiple fields are used as content, wrap each block in a section and concatenate them\n content = contentFields\n .map((field) => `<section id=\"${field}\">${entry[field]}</section>`)\n .join(\"\");\n }\n\n // Store the entry\n store.set({\n id,\n data,\n digest,\n rendered: {\n html: content\n }\n });\n}\n","import type { LoaderContext } from \"astro/loaders\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { isRealtimeData } from \"../utils/is-realtime-data\";\nimport { parseEntry } from \"./parse-entry\";\n\n/**\n * Handles realtime updates for the loader without making any new network requests.\n *\n * Returns `true` if the data was handled and no further action is needed.\n */\nexport async function handleRealtimeUpdates(\n context: LoaderContext,\n options: PocketBaseLoaderOptions\n): Promise<boolean> {\n // Check if a custom filter is set\n if (options.filter) {\n // Updating an entry directly via realtime updates is not supported when using a custom filter.\n // This is because the filter can only be applied via the get request and is not considered in the realtime updates.\n // Updating the entry directly would bypass the filter and could lead to inconsistent data.\n return false;\n }\n\n // Check if data was provided via the refresh context\n if (!context.refreshContextData?.data) {\n return false;\n }\n\n // Check if the data is PocketBase realtime data\n const data = context.refreshContextData.data;\n if (!isRealtimeData(data)) {\n return false;\n }\n\n // Check if the collection name matches the current collection\n if (data.record.collectionName !== options.collectionName) {\n return false;\n }\n\n // Handle deleted entry\n if (data.action === \"delete\") {\n context.logger.info(\"Removing deleted entry\");\n context.store.delete(data.record.id);\n return true;\n }\n\n // Handle updated or new entry\n if (data.action === \"update\") {\n context.logger.info(\"Updating outdated entry\");\n } else {\n context.logger.info(\"Creating new entry\");\n }\n\n // Parse the entry and store\n await parseEntry(data.record, context, options);\n return true;\n}\n","import type { LoaderContext } from \"astro/loaders\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { fetchCollection } from \"./fetch-collection\";\nimport { parseEntry } from \"./parse-entry\";\n\n/**\n * Load (modified) entries from a PocketBase collection.\n *\n * @param options Options for the loader.\n * @param context Context of the loader.\n * @param superuserToken Superuser token to access all resources.\n * @param lastModified Date of the last fetch to only update changed entries.\n */\nexport async function loadEntries(\n options: PocketBaseLoaderOptions,\n context: LoaderContext,\n superuserToken: string | undefined,\n lastModified: string | undefined\n): Promise<void> {\n // Log the fetching of the entries\n context.logger.info(\n `Fetching${lastModified ? \" modified\" : \"\"} data${\n lastModified ? ` starting at ${lastModified}` : \"\"\n }${superuserToken ? \" as superuser\" : \"\"}`\n );\n\n let numEntries = 0;\n await fetchCollection(\n options,\n async (entries) => {\n // Parse and store the entries\n for (const entry of entries) {\n await parseEntry(entry, context, options);\n }\n\n numEntries += entries.length;\n },\n superuserToken,\n {\n lastModified\n }\n );\n\n // Log the number of fetched entries\n if (lastModified) {\n context.logger.info(\n `Updated ${numEntries}/${context.store.keys().length} entries.`\n );\n } else {\n context.logger.info(`Fetched ${numEntries} entries.`);\n }\n}\n","import type { LoaderContext } from \"astro/loaders\";\nimport packageJson from \"../../package.json\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport { shouldRefresh } from \"../utils/should-refresh\";\nimport { cleanupEntries } from \"./cleanup-entries\";\nimport { handleRealtimeUpdates } from \"./handle-realtime-updates\";\nimport { loadEntries } from \"./load-entries\";\n\n/**\n * Load entries from a PocketBase collection.\n */\nexport async function loader(\n context: LoaderContext,\n options: PocketBaseLoaderOptions,\n token: string | undefined\n): Promise<void> {\n context.logger.label = `pocketbase-loader:${options.collectionName}`;\n\n // Check if the collection should be refreshed.\n const refresh = shouldRefresh(\n context.refreshContextData,\n options.collectionName\n );\n if (refresh === \"skip\") {\n return;\n }\n\n // Handle realtime updates\n const handled = await handleRealtimeUpdates(context, options);\n if (handled) {\n return;\n }\n\n // Get the date of the last fetch to only update changed entries.\n let lastModified = context.meta.get(\"last-modified\");\n\n // Force a full update if the refresh is forced\n if (refresh === \"force\") {\n lastModified = undefined;\n context.store.clear();\n }\n\n // Check if the version has changed to force an update\n const lastVersion = context.meta.get(\"version\");\n if (lastVersion !== packageJson.version) {\n if (lastVersion) {\n context.logger.info(\n `PocketBase loader was updated from ${lastVersion} to ${packageJson.version}. All entries will be loaded again.`\n );\n }\n\n // Disable incremental builds and clear the store\n lastModified = undefined;\n context.store.clear();\n }\n\n // Disable incremental builds if no updated field is provided\n if (!options.updatedField) {\n context.logger.info(\n `No \"updatedField\" was provided. Incremental builds are disabled.`\n );\n lastModified = undefined;\n }\n\n if (context.store.keys().length > 0) {\n // Cleanup entries that are no longer in the collection\n await cleanupEntries(options, context, token);\n }\n\n // Load the (modified) entries\n await loadEntries(options, context, token, lastModified);\n\n // Set the last modified date to the current date\n context.meta.set(\"last-modified\", new Date().toISOString().replace(\"T\", \" \"));\n\n context.meta.set(\"version\", packageJson.version);\n}\n","/**\n * Extract field names from fields that may contain modifiers like :excerpt().\n *\n * @param fields Array of field specifications that may contain modifiers\n * @returns Array of clean field names suitable for schema parsing\n */\nexport function extractFieldNames(\n fields: Array<string> | undefined\n): Array<string> | undefined {\n if (!fields) {\n return undefined;\n }\n\n return fields.map((field) => field.split(\":\").at(0) ?? field);\n}\n","import { z } from \"astro/zod\";\n\n/**\n * Entry for a collections schema in PocketBase.\n */\nexport const pocketBaseSchemaEntry = z.object({\n /**\n * Flag to indicate if the field is hidden.\n * Hidden fields are not returned in the API response.\n */\n hidden: z.optional(z.boolean()),\n /**\n * Unique identifier for the field.\n */\n id: z.string(),\n /**\n * Name of the field.\n */\n name: z.string(),\n /**\n * Help text for the field.\n * This is only present if the field has help text defined.\n */\n help: z.optional(z.string()),\n /**\n * Type of the field.\n */\n type: z.enum([\n \"text\",\n \"editor\",\n \"number\",\n \"bool\",\n \"email\",\n \"url\",\n \"date\",\n \"autodate\",\n \"select\",\n \"file\",\n \"relation\",\n \"json\",\n \"geoPoint\",\n \"password\"\n ]),\n /**\n * Whether the field is required.\n */\n required: z.optional(z.boolean()),\n /**\n * Values for a select field.\n * This is only present if the field type is \"select\".\n */\n values: z.optional(z.array(z.string())),\n /**\n * Maximum number of values for a select field.\n * This is only present on \"select\", \"relation\", and \"file\" fields.\n */\n maxSelect: z.optional(z.number()),\n /**\n * Whether the field is filled when the entry is created.\n * This is only present on \"autodate\" fields.\n */\n onCreate: z.optional(z.boolean()),\n /**\n * Whether the field is updated when the entry is updated.\n * This is only present on \"autodate\" fields.\n */\n onUpdate: z.optional(z.boolean())\n});\n\n/**\n * Entry for a collections schema in PocketBase.\n */\nexport type PocketBaseSchemaEntry = z.infer<typeof pocketBaseSchemaEntry>;\n\n/**\n * Schema for a PocketBase collection.\n */\nexport const pocketBaseCollection = z.object({\n /**\n * Name of the collection.\n */\n name: z.string(),\n /**\n * Type of the collection.\n */\n type: z.enum([\"base\", \"view\", \"auth\"]),\n /**\n * Schema of the collection.\n */\n fields: z.array(pocketBaseSchemaEntry)\n});\n\n/**\n * Type for a PocketBase collection.\n */\nexport type PocketBaseCollection = z.infer<typeof pocketBaseCollection>;\n\n/**\n * Schema for an entire PocketBase database.\n */\nexport const pocketBaseDatabase = z.array(pocketBaseCollection);\n","import { pocketBaseErrorResponse } from \"../types/pocketbase-api-response.type\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport type { PocketBaseCollection } from \"../types/pocketbase-schema.type\";\nimport { pocketBaseCollection } from \"../types/pocketbase-schema.type\";\n\n/**\n * Fetches the schema for the specified collection from the PocketBase instance.\n *\n * @param options Options for the loader. See {@link PocketBaseLoaderOptions} for more details.\n * @param token The superuser token to authenticate the request.\n */\nexport async function getRemoteSchema(\n options: Pick<PocketBaseLoaderOptions, \"collectionName\" | \"url\">,\n token: string\n): Promise<PocketBaseCollection | undefined> {\n // Build URL and headers for the schema request\n const schemaUrl = new URL(\n `api/collections/${options.collectionName}`,\n options.url\n ).href;\n const schemaHeaders = new Headers();\n schemaHeaders.set(\"Authorization\", token);\n\n // Fetch the schema\n const schemaRequest = await fetch(schemaUrl, {\n headers: schemaHeaders\n });\n\n // If the request was not successful, try another method\n if (!schemaRequest.ok) {\n const errorResponse = pocketBaseErrorResponse.parse(\n await schemaRequest.json()\n );\n const errorMessage = `Fetching schema from ${options.collectionName} failed with status code ${schemaRequest.status}.\\nReason: ${errorResponse.message}`;\n console.error(errorMessage);\n\n return undefined;\n }\n\n // Get the schema from the response\n const response = pocketBaseCollection.parse(await schemaRequest.json());\n return response;\n}\n","import { z } from \"astro/zod\";\nimport type {\n PocketBaseCollection,\n PocketBaseSchemaEntry\n} from \"../types/pocketbase-schema.type\";\n\nexport interface ParseSchemaOptions {\n hasSuperuserRights: boolean;\n fieldsToInclude?: Array<string>;\n experimentalLiveTypesOnly?: boolean;\n}\n\n/**\n * Converts PocketBase collection fields into Zod types, handling field types, required status, and custom schemas.\n */\nexport function parseSchema(\n collection: PocketBaseCollection,\n customSchemas: Record<string, z.ZodType> | undefined,\n options: ParseSchemaOptions\n): Record<string, z.ZodType> {\n // Prepare the schemas fields\n const fields: Record<string, z.ZodType> = {};\n\n // Parse every field in the schema\n for (const field of collection.fields) {\n // If fieldsToInclude is specified, only include fields that are in the list\n if (\n options.fieldsToInclude &&\n !options.fieldsToInclude.includes(field.name)\n ) {\n continue;\n }\n\n // Skip hidden fields if the user does not have superuser rights\n if (field.hidden && !options.hasSuperuserRights) {\n if (options.fieldsToInclude) {\n console.warn(\n `\"${field.name}\" is requested but hidden. Provide superuser credentials to include this field.`\n );\n }\n\n continue;\n }\n\n let fieldType: z.ZodType;\n\n // Determine the field type and create the corresponding Zod type\n switch (field.type) {\n case \"number\":\n fieldType = z.number();\n break;\n case \"bool\":\n fieldType = z.boolean();\n break;\n case \"date\":\n case \"autodate\":\n if (options.experimentalLiveTypesOnly) {\n // If experimental live types only mode is enabled, treat dates as strings\n fieldType = z.string();\n break;\n }\n // Coerce and parse the value as a date\n fieldType = z.coerce.date();\n break;\n case \"geoPoint\":\n fieldType = z.object({\n lon: z.number(),\n lat: z.number()\n });\n break;\n case \"select\": {\n if (!field.values) {\n throw new Error(\n `Field ${field.name} is of type \"select\" but has no values defined.`\n );\n }\n\n // Create an enum for the select values\n // oxlint-disable-next-line no-unsafe-type-assertion\n const values = z.enum(field.values as [string, ...Array<string>]);\n\n // Parse the field type based on the number of values it can have\n fieldType = parseSingleOrMultipleValues(field, values);\n break;\n }\n case \"relation\":\n case \"file\":\n // NOTE: Relations are currently not supported and are treated as strings\n // NOTE: Files are later transformed to URLs\n\n // Parse the field type based on the number of values it can have\n fieldType = parseSingleOrMultipleValues(field, z.string());\n break;\n case \"json\":\n // Use the user defined custom schema for the field or fallback to unknown\n fieldType = customSchemas?.[field.name] ?? z.unknown();\n break;\n default:\n // Default to a string\n fieldType = z.string();\n break;\n }\n\n const isRequired =\n // Check if the field is required\n field.required ||\n // `onCreate autodate` fields are always set\n (field.type === \"autodate\" && field.onCreate) ||\n // number and bool fields are always set\n field.type === \"number\" ||\n field.type === \"bool\";\n\n // If the field is not required, mark it as optional\n if (!isRequired) {\n fieldType = z.preprocess(\n (val) => val || undefined,\n z.optional(fieldType)\n );\n }\n\n // Add the field to the fields object\n fields[field.name] = fieldType.meta({\n id: field.id,\n title: field.name,\n description: getFieldDescription(field)\n });\n }\n\n return fields;\n}\n\n/**\n * Parse the field type based on the number of values it can have\n *\n * @param field Field to parse\n * @param type Type of each value\n *\n * @returns The parsed field type\n */\nfunction parseSingleOrMultipleValues(\n field: PocketBaseSchemaEntry,\n type: z.ZodType\n): z.ZodType {\n // If the select allows multiple values, create an array of the enum\n if (field.maxSelect === undefined || field.maxSelect === 1) {\n return type;\n }\n\n return z.array(type);\n}\n\n/**\n * Get the description for a field based on its help text and type.\n */\nfunction getFieldDescription(field: PocketBaseSchemaEntry): string | undefined {\n switch (true) {\n case !!field.help:\n return field.help;\n case field.type === \"autodate\" && field.onUpdate:\n return \"Date when the entry was last updated. This field is automatically updated by PocketBase whenever the entry is updated.\";\n case field.type === \"autodate\" && field.onCreate:\n return \"Date when the entry was created. This field is automatically set by PocketBase when the entry is created.\";\n case field.name === \"id\":\n return \"The unique identifier for the entry.\";\n case field.hidden:\n return \"This field is hidden and may require superuser credentials to access.\";\n default:\n return undefined;\n }\n}\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport type { PocketBaseCollection } from \"../types/pocketbase-schema.type\";\nimport { pocketBaseDatabase } from \"../types/pocketbase-schema.type\";\n\n/**\n * Reads the local PocketBase schema file and returns the schema for the specified collection.\n *\n * @param localSchemaPath Path to the local schema file.\n * @param collectionName Name of the collection to get the schema for.\n */\nexport async function readLocalSchema(\n localSchemaPath: string,\n collectionName: string\n): Promise<PocketBaseCollection | undefined> {\n const realPath = path.join(process.cwd(), localSchemaPath);\n\n try {\n // Read the schema file\n const schemaFile = await fs.readFile(realPath, \"utf-8\");\n const fileContent = pocketBaseDatabase.safeParse(JSON.parse(schemaFile));\n\n // Check if the database file is valid\n if (!fileContent.success) {\n throw new Error(\"Invalid schema file\");\n }\n\n // Find and return the schema for the collection\n const schema = fileContent.data.find(\n (collection) => collection.name === collectionName\n );\n\n if (!schema) {\n throw new Error(\n `Collection \"${collectionName}\" not found in schema file`\n );\n }\n\n return schema;\n } catch (error) {\n console.error(\n `Failed to read local schema from ${localSchemaPath}. No types will be generated.\\nReason: ${error}`\n );\n return undefined;\n }\n}\n","import { z } from \"astro/zod\";\nimport type { PocketBaseEntry } from \"../types/pocketbase-entry.type\";\nimport type { PocketBaseSchemaEntry } from \"../types/pocketbase-schema.type\";\n\n/**\n * Transforms file names in a PocketBase entry to file URLs.\n *\n * @param baseUrl URL of the PocketBase instance.\n * @param collection Collection of the entry.\n * @param entry Entry to transform.\n */\nexport function transformFiles(\n baseUrl: string,\n fileFields: Array<PocketBaseSchemaEntry>,\n entry: PocketBaseEntry\n): PocketBaseEntry {\n // Transform all file names to file URLs\n for (const field of fileFields) {\n const fieldName = field.name;\n\n if (field.maxSelect === 1) {\n const fileName = z.optional(z.string()).parse(entry[fieldName]);\n // Check if a file name is present\n if (!fileName) {\n continue;\n }\n\n // Transform the file name to a file URL\n entry[fieldName] = transformFileUrl(\n baseUrl,\n entry.collectionName,\n entry.id,\n fileName\n );\n } else {\n const fileNames = z.optional(z.array(z.string())).parse(entry[fieldName]);\n // Check if file names are present\n if (!fileNames) {\n continue;\n }\n\n // Transform all file names to file URLs\n entry[fieldName] = fileNames.map((file) =>\n transformFileUrl(baseUrl, entry.collectionName, entry.id, file)\n );\n }\n }\n\n return entry;\n}\n\n/**\n * Transforms a file name to a PocketBase file URL.\n *\n * @param base Base URL of the PocketBase instance.\n * @param collectionName Name of the collection.\n * @param entryId ID of the entry.\n * @param file Name of the file.\n */\nexport function transformFileUrl(\n base: string,\n collectionName: string,\n entryId: string,\n file: string\n): string {\n return new URL(`api/files/${collectionName}/${entryId}/${file}`, base).href;\n}\n","import { z } from \"astro/zod\";\nimport type { PocketBaseLoaderOptions } from \"../types/pocketbase-loader-options.type\";\nimport type { PocketBaseCollection } from \"../types/pocketbase-schema.type\";\nimport { combineFieldsForRequest } from \"../utils/combine-fields-for-request\";\nimport { extractFieldNames } from \"../utils/extract-field-names\";\nimport { formatFields } from \"../utils/format-fields\";\nimport { getRemoteSchema } from \"./get-remote-schema\";\nimport { parseSchema } from \"./parse-schema\";\nimport { readLocalSchema } from \"./read-local-schema\";\nimport { transformFiles } from \"./transform-files\";\n\n/**\n * Basic schema for every PocketBase collection.\n */\nconst BASIC_SCHEMA = z.object({\n id: z.string().meta({\n title: \"id\",\n description: \"The unique identifier for the entry.\"\n }),\n collectionId: z.string().meta({\n title: \"collectionId\",\n description:\n \"The unique identifier for the collection the entity belongs to.\"\n }),\n collectionName: z.string().meta({\n title: \"collectionName\",\n description: \"The name of the collection the entity belongs to.\"\n })\n});\n\n/**\n * Types of fields that can be used as an ID.\n */\nconst VALID_ID_TYPES = [\"text\", \"number\", \"email\", \"url\", \"date\"];\n\n/**\n * Generate a schema for the collection based on the collection's schema in PocketBase.\n * By default, a basic schema is returned if no other schema is available.\n * If superuser credentials are provided, the schema is fetched from the PocketBase API.\n * If a path to a local schema file is provided, the schema is read from the file.\n *\n * @param options Options for the loader. See {@link PocketBaseLoaderOptions} for more details.\n * @param token The superuser token to authenticate the request.\n */\n// oxlint-disable-next-line explicit-module-boundary-types\nexport async function generateSchema(\n options: PocketBaseLoaderOptions,\n token: string | undefined\n) {\n let collection: PocketBaseCollection | undefined;\n\n if (token) {\n // Try to get the schema directly from the PocketBase instance\n collection = await getRemoteSchema(options, token);\n }\n\n const hasSuperuserRights = !!collection || !!options.superuserCredentials;\n\n // If the schema is not available, try to read it from a local schema file\n if (!collection && options.localSchema) {\n collection = await readLocalSchema(\n options.localSchema,\n options.collectionName\n );\n }\n\n // If the schema is still not available, return the basic schema\n if (!collection) {\n console.error(\n `No schema available for \"${options.collectionName}\". Only basic types are available. Please check your configuration and provide a valid schema file or superuser credentials.`\n );\n // Return the basic schema since every collection has at least these fields\n return BASIC_SCHEMA;\n }\n\n // Get fields to include from options\n const formattedFields = formatFields(options.fields);\n const fieldNames = extractFieldNames(formattedFields);\n const fieldsToInclude = combineFieldsForRequest(fieldNames, options);\n\n // Parse the schema with optional field filtering\n const fields = parseSchema(collection, options.jsonSchemas, {\n hasSuperuserRights,\n fieldsToInclude,\n experimentalLiveTypesOnly: options.experimental?.liveTypesOnly\n });\n\n // Do some sanity checks on the provided options\n checkCustomIdField(collection, options);\n checkContentField(fields, options);\n checkUpdatedField(fields, collection, options);\n\n // Combine the basic schema with the parsed fields\n const schema = z.object({\n ...BASIC_SCHEMA.shape,\n ...fields\n });\n\n // Get all file fields\n const fileFields = collection.fields\n .filter((field) => field.type === \"file\")\n // Only show hidden fields if the user has superuser rights\n .filter((field) => !field.hidden || hasSuperuserRights);\n\n if (fileFields.length === 0) {\n return schema;\n }\n\n // Transform file names to file urls\n return schema.transform((entry) =>\n transformFiles(options.url, fileFields, entry)\n );\n}\n\n/**\n * Check if the custom id field is present\n */\nfunction checkCustomIdField(\n collection: PocketBaseCollection,\n options: PocketBaseLoaderOptions\n): void {\n if (!options.idField) {\n return;\n }\n\n // Find the id field in the schema\n const idField = collection.fields.find(\n (field) => field.name === options.idField\n );\n\n // Check if the id field is present and of a valid type\n if (!idField) {\n console.error(\n `The id field \"${options.idField}\" is not present in the schema of the collection \"${options.collectionName}\".`\n );\n } else if (!VALID_ID_TYPES.includes(idField.type)) {\n console.error(\n `The id field \"${options.idField}\" for collection \"${\n options.collectionName\n }\" is of type \"${\n idField.type\n }\" which is not recommended. Please use one of the following types: ${VALID_ID_TYPES.join(\n \", \"\n )}.`\n );\n }\n}\n\n/**\n * Check if the content field(s) are present\n */\nfunction checkContentField(\n fields: Record<string, z.ZodType>,\n options: PocketBaseLoaderOptions\n): void {\n if (\n typeof options.contentFields === \"string\" &&\n !fields[options.contentFields]\n ) {\n console.error(\n `The content field \"${options.contentFields}\" is not present in the schema of the collection \"${options.collectionName}\".`\n );\n } else if (Array.isArray(options.contentFields)) {\n for (const field of options.contentFields) {\n if (!fields[field]) {\n console.error(\n `The content field \"${field}\" is not present in the schema of the collection \"${options.collectionName}\".`\n );\n }\n }\n }\n}\n\n/**\n * Check if the updated field is present\n */\nfunction checkUpdatedField(\n fields: Record<string, z.ZodType>,\n collection: PocketBaseCollection,\n options: PocketBaseLoaderOptions\n): void {\n if (!options.updatedField) {\n return;\n }\n\n if (!fields[options.updatedField]) {\n console.error(\n `The field \"${options.updatedField}\" is not present in the schema of the collection \"${options.collectionName}\".\\nThis will lead to errors when trying to fetch only updated entries.`\n );\n } else {\n const updatedField = collection.fields.find(\n (field) => field.name === options.updatedField\n );\n if (updatedField?.type !== \"autodate\" || !updatedField.onUpdate) {\n console.warn(\n `The field \"${options.updatedField}\" is not of type \"autodate\" with the value \"Update\" or \"Create/Update\".\\nMake sure that the field is automatically updated when the entry is updated!`\n );\n }\n }\n}\n","import type { ZodObject, ZodPipe } from \"astro/zod\";\nimport {\n createAuxiliaryTypeStore,\n createTypeAlias,\n printNode,\n zodToTs\n} from \"zod-to-ts\";\n\n/**\n * Generate a TypeScript type from a Zod schema.\n */\nexport function generateType(schema: ZodObject | ZodPipe): string {\n // If the collection contains file fields, we transform the field values to file urls in a `transform` step.\n // This means that the schema for the types is different from the schema for the data, so we need to extract the inner schema for the types.\n const schemaForTypes = schema.type === \"pipe\" ? schema.in : schema;\n\n const { node } = zodToTs(schemaForTypes, {\n auxiliaryTypeStore: createAuxiliaryTypeStore()\n });\n const typeAlias = createTypeAlias(node, \"Entry\");\n\n return `export ${printNode(typeAlias)}`;\n}\n","import type { AstroIntegrationLogger } from \"astro\";\nimport {\n pocketBaseErrorResponse,\n pocketBaseLoginResponse\n} from \"../types/pocketbase-api-response.type\";\n\n/**\n * This function will get a superuser token from the given PocketBase instance.\n *\n * @param url URL of the PocketBase instance\n * @param superuserCredentials Credentials of the superuser\n *\n * @returns A superuser token to access all resources of the PocketBase instance.\n */\nexport async function getSuperuserToken(\n url: string,\n superuserCredentials: {\n email: string;\n password: string;\n },\n logger?: AstroIntegrationLogger\n): Promise<string | undefined> {\n // Build the URL for the login endpoint\n const loginUrl = new URL(\n `api/collections/_superusers/auth-with-password`,\n url\n ).href;\n\n // Create a new FormData object to send the login data\n const loginData = new FormData();\n loginData.set(\"identity\", superuserCredentials.email);\n loginData.set(\"password\", superuserCredentials.password);\n\n // Send the login request to get a token\n const loginRequest = await fetch(loginUrl, {\n method: \"POST\",\n body: loginData\n });\n\n // If the login request was not successful, print the error message and return undefined\n if (!loginRequest.ok) {\n if (loginRequest.status === 429) {\n const info =\n \"A rate limit was hit while trying to authenticate with PocketBase. Consider using an `impersonateToken` as credentials to avoid this issue.\";\n if (logger) {\n logger.info(info);\n } else {\n console.info(info);\n }\n\n // Random wait between 3 (default rate limit interval) and 8 seconds\n const retryAfter = Math.random() * 5 + 3;\n // oxlint-disable-next-line promise/avoid-new\n await new Promise((resolve) => {\n setTimeout(resolve, retryAfter * 1000);\n });\n\n return getSuperuserToken(url, superuserCredentials, logger);\n }\n\n const errorResponse = pocketBaseErrorResponse.parse(\n await loginRequest.json()\n );\n const errorMessage = `The given email / password for ${url} was not correct. Astro can't generate type definitions automatically and may not have access to all resources.\\nReason: ${errorResponse.message}`;\n if (logger) {\n logger.error(errorMessage);\n } else {\n console.error(errorMessage);\n }\n return undefined;\n }\n\n // Return the token\n const response = pocketBaseLoginResponse.parse(await loginRequest.json());\n return response.token;\n}\n","import type { PocketBaseLoaderBaseOptions } from \"../types/pocketbase-loader-options.type\";\nimport { getSuperuserToken } from \"./get-superuser-token\";\n\n/**\n * Creates a promise that resolves to a superuser token or undefined.\n */\nexport async function createTokenPromise(\n options: Pick<PocketBaseLoaderBaseOptions, \"superuserCredentials\" | \"url\">\n): Promise<string | undefined> {\n if (options.superuserCredentials) {\n if (\"impersonateToken\" in options.superuserCredentials) {\n // Impersonate token provided, so use it directly.\n return options.superuserCredentials.impersonateToken;\n }\n // Email and password provided, so get a temporary superuser token.\n const token = await getSuperuserToken(\n options.url,\n options.superuserCredentials\n );\n return token;\n }\n\n // No credentials provided, so no token can be used.\n return undefined;\n}\n","import type { LiveDataCollection, LiveDataEntry } from \"astro\";\nimport type { LiveCollectionError } from \"astro/content/runtime\";\nimport type { LiveLoader, Loader, LoaderContext } from \"astro/loaders\";\nimport { liveCollectionLoader } from \"./loader/live-collection-loader\";\nimport { liveEntryLoader } from \"./loader/live-entry-loader\";\nimport { loader } from \"./loader/loader\";\nimport { generateSchema } from \"./schema/generate-schema\";\nimport { generateType } from \"./schema/generate-type\";\nimport type { PocketBaseEntry } from \"./types/pocketbase-entry.type\";\nimport type {\n PocketBaseLiveLoaderCollectionFilter,\n PocketBaseLiveLoaderEntryFilter\n} from \"./types/pocketbase-live-loader-filter.type\";\nimport type {\n PocketBaseLiveLoaderOptions,\n PocketBaseLoaderOptions\n} from \"./types/pocketbase-loader-options.type\";\nimport { createTokenPromise } from \"./utils/create-token-promise\";\n\n/**\n * Loader for collections stored in PocketBase.\n *\n * @param options Options for the loader. See {@link PocketBaseLoaderOptions} for more details.\n */\n// oxlint-disable-next-line explicit-module-boundary-types\nexport function pocketbaseLoader(options: PocketBaseLoaderOptions) {\n // Create shared promise for the superuser token, which can be reused\n const tokenPromise = createTokenPromise(options);\n\n return {\n name: \"pocketbase-loader\",\n load: async (context: LoaderContext): Promise<void> => {\n if (options.experimental?.liveTypesOnly) {\n context.logger.label = `pocketbase-loader:${options.collectionName}`;\n context.logger.info(\n \"Experimental live types only mode enabled. No data will be loaded, only types will be generated.\"\n );\n return;\n }\n\n const token = await tokenPromise;\n\n // Load the entries from the collection\n await loader(context, options, token);\n },\n createSchema: async () => {\n const token = await tokenPromise;\n\n // Generate the schema for the collection according to the API\n const schema = await generateSchema(options, token);\n const types = generateType(schema);\n\n return {\n schema,\n types\n };\n }\n } satisfies Loader;\n}\n\n/**\n * Live loader for collections stored in PocketBase.\n *\n * @param options Options for the live loader. See {@link PocketBaseLiveLoaderOptions} for more details.\n */\nexport function pocketbaseLiveLoader<TEntry extends PocketBaseEntry>(\n options: PocketBaseLiveLoaderOptions\n): LiveLoader<\n TEntry,\n PocketBaseLiveLoaderEntryFilter,\n PocketBaseLiveLoaderCollectionFilter,\n LiveCollectionError\n> {\n // Create shared promise for the superuser token, which can be reused\n const tokenPromise = createTokenPromise(options);\n\n return {\n name: \"pocketbase-live-loader\",\n loadCollection: async ({\n filter\n }): Promise<\n LiveDataCollection<TEntry> | { error: LiveCollectionError }\n > => {\n const token = await tokenPromise;\n\n // Load entries from the collection\n return liveCollectionLoader<TEntry>(filter, options, token);\n },\n loadEntry: async ({\n filter\n }): Promise<LiveDataEntry<TEntry> | { error: LiveCollectionError }> => {\n const token = await tokenPromise;\n\n // Load a single entry from the collection\n return liveEntryLoader<TEntry>(filter.id, options, token);\n }\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-loader-pocketbase",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2-next.2",
|
|
4
4
|
"description": "A content loader for Astro that uses the PocketBase API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -20,22 +20,26 @@
|
|
|
20
20
|
"url": "git+https://github.com/pawcoding/astro-loader-pocketbase.git"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"
|
|
23
|
+
"dist"
|
|
24
24
|
],
|
|
25
25
|
"type": "module",
|
|
26
|
+
"types": "./dist/index.d.mts",
|
|
26
27
|
"exports": {
|
|
27
|
-
".": "./
|
|
28
|
+
".": "./dist/index.mjs",
|
|
29
|
+
"./package.json": "./package.json"
|
|
28
30
|
},
|
|
29
31
|
"publishConfig": {
|
|
30
32
|
"access": "public",
|
|
31
33
|
"provenance": true
|
|
32
34
|
},
|
|
33
35
|
"scripts": {
|
|
36
|
+
"build": "tsdown",
|
|
34
37
|
"format": "oxfmt",
|
|
35
38
|
"format:check": "oxfmt --check",
|
|
36
39
|
"lint": "oxlint",
|
|
37
40
|
"lint:fix": "oxlint --fix",
|
|
38
41
|
"prepare": "husky",
|
|
42
|
+
"prepublishOnly": "npm run build",
|
|
39
43
|
"test": "vitest run",
|
|
40
44
|
"test:e2e": "vitest run $(find test -name '*.e2e-spec.ts')",
|
|
41
45
|
"test:e2e:pocketbase": "npm run test:e2e:setup && ./.pocketbase/pocketbase serve",
|
|
@@ -47,23 +51,31 @@
|
|
|
47
51
|
"typecheck": "npx @typescript/native-preview --noEmit -p src/tsconfig.json && npx @typescript/native-preview --noEmit -p test/tsconfig.json"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
|
-
"@commitlint/cli": "
|
|
51
|
-
"@commitlint/config-conventional": "
|
|
52
|
-
"@types/node": "24.
|
|
53
|
-
"@vitest/coverage-v8": "4.1.
|
|
54
|
-
"astro": "6.2
|
|
54
|
+
"@commitlint/cli": "21.0.2",
|
|
55
|
+
"@commitlint/config-conventional": "21.0.2",
|
|
56
|
+
"@types/node": "24.12.4",
|
|
57
|
+
"@vitest/coverage-v8": "4.1.7",
|
|
58
|
+
"astro": "6.4.2",
|
|
55
59
|
"globals": "17.6.0",
|
|
56
60
|
"husky": "9.1.7",
|
|
57
|
-
"lint-staged": "
|
|
58
|
-
"oxfmt": "0.
|
|
59
|
-
"oxlint": "1.
|
|
60
|
-
"oxlint-tsgolint": "0.
|
|
61
|
-
"
|
|
61
|
+
"lint-staged": "17.0.6",
|
|
62
|
+
"oxfmt": "0.52.0",
|
|
63
|
+
"oxlint": "1.67.0",
|
|
64
|
+
"oxlint-tsgolint": "0.23.0",
|
|
65
|
+
"publint": "0.3.21",
|
|
66
|
+
"tsdown": "0.22.1",
|
|
67
|
+
"vitest": "4.1.7",
|
|
62
68
|
"zod-to-ts": "2.0.0"
|
|
63
69
|
},
|
|
64
70
|
"peerDependencies": {
|
|
65
71
|
"astro": "^6.0.0",
|
|
66
72
|
"zod-to-ts": "^2.0.0"
|
|
67
73
|
},
|
|
68
|
-
"
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=22.12.0"
|
|
76
|
+
},
|
|
77
|
+
"packageManager": "npm@11.16.0",
|
|
78
|
+
"allowScripts": {
|
|
79
|
+
"esbuild@0.27.7": true
|
|
80
|
+
}
|
|
69
81
|
}
|
package/src/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { pocketbaseLiveLoader, pocketbaseLoader } from "./pocketbase-loader";
|
|
2
|
-
import { transformFileUrl } from "./schema/transform-files";
|
|
3
|
-
import { PocketBaseAuthenticationError } from "./types/errors";
|
|
4
|
-
import type {
|
|
5
|
-
PocketBaseLiveLoaderCollectionFilter,
|
|
6
|
-
PocketBaseLiveLoaderEntryFilter
|
|
7
|
-
} from "./types/pocketbase-live-loader-filter.type";
|
|
8
|
-
import type {
|
|
9
|
-
PocketBaseLiveLoaderOptions,
|
|
10
|
-
PocketBaseLoaderOptions
|
|
11
|
-
} from "./types/pocketbase-loader-options.type";
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
PocketBaseAuthenticationError,
|
|
15
|
-
pocketbaseLiveLoader,
|
|
16
|
-
pocketbaseLoader,
|
|
17
|
-
transformFileUrl
|
|
18
|
-
};
|
|
19
|
-
export type {
|
|
20
|
-
PocketBaseLiveLoaderCollectionFilter,
|
|
21
|
-
PocketBaseLiveLoaderEntryFilter,
|
|
22
|
-
PocketBaseLiveLoaderOptions,
|
|
23
|
-
PocketBaseLoaderOptions
|
|
24
|
-
};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import type { LoaderContext } from "astro/loaders";
|
|
2
|
-
import { z } from "astro/zod";
|
|
3
|
-
import {
|
|
4
|
-
pocketBaseErrorResponse,
|
|
5
|
-
pocketBaseListResponse
|
|
6
|
-
} from "../types/pocketbase-api-response.type";
|
|
7
|
-
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
8
|
-
import { pocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
9
|
-
import type { PocketBaseLoaderBaseOptions } from "../types/pocketbase-loader-options.type";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Cleanup entries that are no longer in the collection.
|
|
13
|
-
*
|
|
14
|
-
* @param options Options for the loader.
|
|
15
|
-
* @param context Context of the loader.
|
|
16
|
-
* @param superuserToken Superuser token to access all resources.
|
|
17
|
-
*/
|
|
18
|
-
export async function cleanupEntries(
|
|
19
|
-
options: PocketBaseLoaderBaseOptions,
|
|
20
|
-
context: LoaderContext,
|
|
21
|
-
superuserToken: string | undefined
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
// Build the URL for the collections endpoint
|
|
24
|
-
const collectionUrl = new URL(
|
|
25
|
-
`api/collections/${options.collectionName}/records`,
|
|
26
|
-
options.url
|
|
27
|
-
).href;
|
|
28
|
-
|
|
29
|
-
// Create the headers for the request to append the superuser token (if available)
|
|
30
|
-
const collectionHeaders = new Headers();
|
|
31
|
-
if (superuserToken) {
|
|
32
|
-
collectionHeaders.set("Authorization", superuserToken);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Prepare pagination variables
|
|
36
|
-
let page = 0;
|
|
37
|
-
let totalPages = 0;
|
|
38
|
-
const entries = new Set<string>();
|
|
39
|
-
|
|
40
|
-
// Fetch all ids of the collection
|
|
41
|
-
do {
|
|
42
|
-
// Build search parameters
|
|
43
|
-
const searchParams = new URLSearchParams({
|
|
44
|
-
page: `${++page}`,
|
|
45
|
-
perPage: "1000",
|
|
46
|
-
fields: "id"
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (options.filter) {
|
|
50
|
-
// If a filter is set, add it to the search parameters
|
|
51
|
-
searchParams.set("filter", `(${options.filter})`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Fetch ids from the collection
|
|
55
|
-
const collectionRequest = await fetch(
|
|
56
|
-
`${collectionUrl}?${searchParams.toString()}`,
|
|
57
|
-
{
|
|
58
|
-
headers: collectionHeaders
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// If the request was not successful, print the error message and return
|
|
63
|
-
if (!collectionRequest.ok) {
|
|
64
|
-
// If the collection is locked, an superuser token is required
|
|
65
|
-
if (collectionRequest.status === 403) {
|
|
66
|
-
if (
|
|
67
|
-
options.superuserCredentials &&
|
|
68
|
-
"impersonateToken" in options.superuserCredentials
|
|
69
|
-
) {
|
|
70
|
-
context.logger.error("The given impersonate token is not valid.");
|
|
71
|
-
} else {
|
|
72
|
-
context.logger.error(
|
|
73
|
-
"The collection is not accessible without superuser rights. Please provide superuser credentials in the config."
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
const errorResponse = pocketBaseErrorResponse.parse(
|
|
78
|
-
await collectionRequest.json()
|
|
79
|
-
);
|
|
80
|
-
const errorMessage = `Fetching ids failed with status code ${collectionRequest.status}.\nReason: ${errorResponse.message}`;
|
|
81
|
-
context.logger.error(errorMessage);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Remove all entries from the store
|
|
85
|
-
context.logger.info(`Removing all entries from the store.`);
|
|
86
|
-
context.store.clear();
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Get the data from the response
|
|
91
|
-
const response = cleanUpEntriesResponse.parse(
|
|
92
|
-
await collectionRequest.json()
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
// Add the ids to the set
|
|
96
|
-
for (const item of response.items) {
|
|
97
|
-
entries.add(item.id);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Update the page and total pages
|
|
101
|
-
page = response.page;
|
|
102
|
-
totalPages = response.totalPages;
|
|
103
|
-
} while (page < totalPages);
|
|
104
|
-
|
|
105
|
-
let cleanedUp = 0;
|
|
106
|
-
|
|
107
|
-
// Create a mapping from PocketBase IDs to store keys for proper cleanup
|
|
108
|
-
const storedIds = new Map<string, string>(
|
|
109
|
-
context.store
|
|
110
|
-
.values()
|
|
111
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
112
|
-
.map((entry) => [(entry.data as PocketBaseEntry).id, entry.id])
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
// Check which PocketBase IDs are missing from the server response
|
|
116
|
-
for (const [pocketbaseId, storeKey] of storedIds.entries()) {
|
|
117
|
-
// If the PocketBase ID is not in the entries set, remove the entry from the store
|
|
118
|
-
if (!entries.has(pocketbaseId)) {
|
|
119
|
-
context.store.delete(storeKey);
|
|
120
|
-
cleanedUp++;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (cleanedUp > 0) {
|
|
125
|
-
// Log the number of cleaned up entries
|
|
126
|
-
context.logger.info(`Cleaned up ${cleanedUp} old entries.`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The response schema for cleaning up entries.
|
|
132
|
-
*/
|
|
133
|
-
const cleanUpEntriesResponse = pocketBaseListResponse
|
|
134
|
-
.omit({ items: true })
|
|
135
|
-
.extend({
|
|
136
|
-
items: z.array(pocketBaseEntry.pick({ id: true }))
|
|
137
|
-
});
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LiveCollectionError,
|
|
3
|
-
LiveEntryNotFoundError
|
|
4
|
-
} from "astro/content/runtime";
|
|
5
|
-
import { PocketBaseAuthenticationError } from "../types/errors";
|
|
6
|
-
import {
|
|
7
|
-
pocketBaseErrorResponse,
|
|
8
|
-
pocketBaseListResponse
|
|
9
|
-
} from "../types/pocketbase-api-response.type";
|
|
10
|
-
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
11
|
-
import type { PocketBaseLiveLoaderCollectionFilter } from "../types/pocketbase-live-loader-filter.type";
|
|
12
|
-
import type { PocketBaseLoaderBaseOptions } from "../types/pocketbase-loader-options.type";
|
|
13
|
-
import { combineFieldsForRequest } from "../utils/combine-fields-for-request";
|
|
14
|
-
import { formatFields } from "../utils/format-fields";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Provides utilities to fetch entries from a PocketBase collection, supporting filtering and pagination.
|
|
18
|
-
*/
|
|
19
|
-
export type CollectionFilter = {
|
|
20
|
-
/**
|
|
21
|
-
* Date string to only fetch entries that have been modified since this date.
|
|
22
|
-
* If not provided, all entries will be fetched.
|
|
23
|
-
*/
|
|
24
|
-
lastModified?: string;
|
|
25
|
-
} & PocketBaseLiveLoaderCollectionFilter;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Fetches entries from a PocketBase collection, optionally filtering by modification date and supporting pagination.
|
|
29
|
-
*/
|
|
30
|
-
export async function fetchCollection<TEntry extends PocketBaseEntry>(
|
|
31
|
-
options: PocketBaseLoaderBaseOptions,
|
|
32
|
-
chunkLoaded: (entries: Array<TEntry>) => Promise<void>,
|
|
33
|
-
token: string | undefined,
|
|
34
|
-
collectionFilter: CollectionFilter | undefined
|
|
35
|
-
): Promise<void> {
|
|
36
|
-
// Build the URL for the collections endpoint
|
|
37
|
-
const collectionUrl = new URL(
|
|
38
|
-
`api/collections/${options.collectionName}/records`,
|
|
39
|
-
options.url
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// Create the headers for the request to append the token (if available)
|
|
43
|
-
const collectionHeaders = new Headers();
|
|
44
|
-
if (token) {
|
|
45
|
-
collectionHeaders.set("Authorization", token);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Cache fields computation outside the loop
|
|
49
|
-
const fieldsArray = formatFields(options.fields);
|
|
50
|
-
const combinedFields = combineFieldsForRequest(fieldsArray, options);
|
|
51
|
-
|
|
52
|
-
// Prepare pagination variables
|
|
53
|
-
let page = 0;
|
|
54
|
-
let totalPages = 0;
|
|
55
|
-
|
|
56
|
-
// Fetch all (modified) entries
|
|
57
|
-
do {
|
|
58
|
-
const searchParams = buildSearchParams(options, combinedFields, {
|
|
59
|
-
...collectionFilter,
|
|
60
|
-
page: collectionFilter?.page ?? ++page,
|
|
61
|
-
perPage: collectionFilter?.perPage ?? 100
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Apply search parameters to URL
|
|
65
|
-
collectionUrl.search = searchParams.toString();
|
|
66
|
-
|
|
67
|
-
// Fetch entries from the collection
|
|
68
|
-
const collectionRequest = await fetch(collectionUrl.href, {
|
|
69
|
-
headers: collectionHeaders
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// If the request was not successful, print the error message and return
|
|
73
|
-
if (!collectionRequest.ok) {
|
|
74
|
-
// If the collection is locked, an superuser token is required
|
|
75
|
-
if (collectionRequest.status === 403) {
|
|
76
|
-
if (
|
|
77
|
-
options.superuserCredentials &&
|
|
78
|
-
"impersonateToken" in options.superuserCredentials
|
|
79
|
-
) {
|
|
80
|
-
throw new PocketBaseAuthenticationError(
|
|
81
|
-
options.collectionName,
|
|
82
|
-
"The given impersonate token is not valid."
|
|
83
|
-
);
|
|
84
|
-
} else {
|
|
85
|
-
throw new PocketBaseAuthenticationError(
|
|
86
|
-
options.collectionName,
|
|
87
|
-
"The collection is not accessible without superuser rights. Please provide superuser credentials in the config."
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (collectionRequest.status === 404) {
|
|
93
|
-
throw new LiveEntryNotFoundError(options.collectionName, {
|
|
94
|
-
...collectionFilter
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Get the reason for the error
|
|
99
|
-
const errorResponse = pocketBaseErrorResponse.parse(
|
|
100
|
-
await collectionRequest.json()
|
|
101
|
-
);
|
|
102
|
-
throw new LiveCollectionError(
|
|
103
|
-
options.collectionName,
|
|
104
|
-
errorResponse.message
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Get the data from the response
|
|
109
|
-
const response = pocketBaseListResponse.parse(
|
|
110
|
-
await collectionRequest.json()
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
// Return current chunk
|
|
114
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
115
|
-
await chunkLoaded(response.items as Array<TEntry>);
|
|
116
|
-
|
|
117
|
-
// Update the page and total pages
|
|
118
|
-
page = response.page;
|
|
119
|
-
totalPages = response.totalPages;
|
|
120
|
-
} while (!collectionFilter?.perPage && page < totalPages);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Build search parameters for the PocketBase collection request.
|
|
125
|
-
*/
|
|
126
|
-
function buildSearchParams(
|
|
127
|
-
loaderOptions: PocketBaseLoaderBaseOptions,
|
|
128
|
-
combinedFields: Array<string> | undefined,
|
|
129
|
-
collectionFilter: CollectionFilter
|
|
130
|
-
): URLSearchParams {
|
|
131
|
-
const searchParams = new URLSearchParams();
|
|
132
|
-
|
|
133
|
-
if (collectionFilter.page) {
|
|
134
|
-
searchParams.set("page", `${collectionFilter.page}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (collectionFilter.perPage) {
|
|
138
|
-
searchParams.set("perPage", `${collectionFilter.perPage}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const filters = [];
|
|
142
|
-
|
|
143
|
-
// If `lastModified` is set, only fetch entries that have been modified since the last fetch
|
|
144
|
-
// Sort by the updated field and id
|
|
145
|
-
if (collectionFilter.lastModified && loaderOptions.updatedField) {
|
|
146
|
-
filters.push(
|
|
147
|
-
`(${loaderOptions.updatedField}>"${collectionFilter.lastModified}")`
|
|
148
|
-
);
|
|
149
|
-
searchParams.set("sort", `-${loaderOptions.updatedField},id`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Add filter from the loader options
|
|
153
|
-
if (loaderOptions.filter) {
|
|
154
|
-
filters.push(`(${loaderOptions.filter})`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Add additional filter from the collection filter
|
|
158
|
-
if (collectionFilter.filter) {
|
|
159
|
-
filters.push(`(${collectionFilter.filter})`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Add filters to search parameters
|
|
163
|
-
if (filters.length > 0) {
|
|
164
|
-
searchParams.set("filter", filters.join("&&"));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (collectionFilter.sort) {
|
|
168
|
-
searchParams.set("sort", collectionFilter.sort);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Add fields parameter if specified
|
|
172
|
-
if (combinedFields) {
|
|
173
|
-
searchParams.set("fields", combinedFields.join(","));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return searchParams;
|
|
177
|
-
}
|