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
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod";
|
|
2
|
-
import type { PocketBaseLoaderOptions } from "../types/pocketbase-loader-options.type";
|
|
3
|
-
import type { PocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
4
|
-
import { combineFieldsForRequest } from "../utils/combine-fields-for-request";
|
|
5
|
-
import { extractFieldNames } from "../utils/extract-field-names";
|
|
6
|
-
import { formatFields } from "../utils/format-fields";
|
|
7
|
-
import { getRemoteSchema } from "./get-remote-schema";
|
|
8
|
-
import { parseSchema } from "./parse-schema";
|
|
9
|
-
import { readLocalSchema } from "./read-local-schema";
|
|
10
|
-
import { transformFiles } from "./transform-files";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Basic schema for every PocketBase collection.
|
|
14
|
-
*/
|
|
15
|
-
const BASIC_SCHEMA = z.object({
|
|
16
|
-
id: z.string().meta({
|
|
17
|
-
title: "id",
|
|
18
|
-
description: "The unique identifier for the entry."
|
|
19
|
-
}),
|
|
20
|
-
collectionId: z.string().meta({
|
|
21
|
-
title: "collectionId",
|
|
22
|
-
description:
|
|
23
|
-
"The unique identifier for the collection the entity belongs to."
|
|
24
|
-
}),
|
|
25
|
-
collectionName: z.string().meta({
|
|
26
|
-
title: "collectionName",
|
|
27
|
-
description: "The name of the collection the entity belongs to."
|
|
28
|
-
})
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Types of fields that can be used as an ID.
|
|
33
|
-
*/
|
|
34
|
-
const VALID_ID_TYPES = ["text", "number", "email", "url", "date"];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Generate a schema for the collection based on the collection's schema in PocketBase.
|
|
38
|
-
* By default, a basic schema is returned if no other schema is available.
|
|
39
|
-
* If superuser credentials are provided, the schema is fetched from the PocketBase API.
|
|
40
|
-
* If a path to a local schema file is provided, the schema is read from the file.
|
|
41
|
-
*
|
|
42
|
-
* @param options Options for the loader. See {@link PocketBaseLoaderOptions} for more details.
|
|
43
|
-
* @param token The superuser token to authenticate the request.
|
|
44
|
-
*/
|
|
45
|
-
// oxlint-disable-next-line explicit-module-boundary-types
|
|
46
|
-
export async function generateSchema(
|
|
47
|
-
options: PocketBaseLoaderOptions,
|
|
48
|
-
token: string | undefined
|
|
49
|
-
) {
|
|
50
|
-
let collection: PocketBaseCollection | undefined;
|
|
51
|
-
|
|
52
|
-
if (token) {
|
|
53
|
-
// Try to get the schema directly from the PocketBase instance
|
|
54
|
-
collection = await getRemoteSchema(options, token);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const hasSuperuserRights = !!collection || !!options.superuserCredentials;
|
|
58
|
-
|
|
59
|
-
// If the schema is not available, try to read it from a local schema file
|
|
60
|
-
if (!collection && options.localSchema) {
|
|
61
|
-
collection = await readLocalSchema(
|
|
62
|
-
options.localSchema,
|
|
63
|
-
options.collectionName
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// If the schema is still not available, return the basic schema
|
|
68
|
-
if (!collection) {
|
|
69
|
-
console.error(
|
|
70
|
-
`No schema available for "${options.collectionName}". Only basic types are available. Please check your configuration and provide a valid schema file or superuser credentials.`
|
|
71
|
-
);
|
|
72
|
-
// Return the basic schema since every collection has at least these fields
|
|
73
|
-
return BASIC_SCHEMA;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Get fields to include from options
|
|
77
|
-
const formattedFields = formatFields(options.fields);
|
|
78
|
-
const fieldNames = extractFieldNames(formattedFields);
|
|
79
|
-
const fieldsToInclude = combineFieldsForRequest(fieldNames, options);
|
|
80
|
-
|
|
81
|
-
// Parse the schema with optional field filtering
|
|
82
|
-
const fields = parseSchema(collection, options.jsonSchemas, {
|
|
83
|
-
hasSuperuserRights,
|
|
84
|
-
fieldsToInclude,
|
|
85
|
-
experimentalLiveTypesOnly: options.experimental?.liveTypesOnly
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Do some sanity checks on the provided options
|
|
89
|
-
checkCustomIdField(collection, options);
|
|
90
|
-
checkContentField(fields, options);
|
|
91
|
-
checkUpdatedField(fields, collection, options);
|
|
92
|
-
|
|
93
|
-
// Combine the basic schema with the parsed fields
|
|
94
|
-
const schema = z.object({
|
|
95
|
-
...BASIC_SCHEMA.shape,
|
|
96
|
-
...fields
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Get all file fields
|
|
100
|
-
const fileFields = collection.fields
|
|
101
|
-
.filter((field) => field.type === "file")
|
|
102
|
-
// Only show hidden fields if the user has superuser rights
|
|
103
|
-
.filter((field) => !field.hidden || hasSuperuserRights);
|
|
104
|
-
|
|
105
|
-
if (fileFields.length === 0) {
|
|
106
|
-
return schema;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Transform file names to file urls
|
|
110
|
-
return schema.transform((entry) =>
|
|
111
|
-
transformFiles(options.url, fileFields, entry)
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Check if the custom id field is present
|
|
117
|
-
*/
|
|
118
|
-
function checkCustomIdField(
|
|
119
|
-
collection: PocketBaseCollection,
|
|
120
|
-
options: PocketBaseLoaderOptions
|
|
121
|
-
): void {
|
|
122
|
-
if (!options.idField) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Find the id field in the schema
|
|
127
|
-
const idField = collection.fields.find(
|
|
128
|
-
(field) => field.name === options.idField
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Check if the id field is present and of a valid type
|
|
132
|
-
if (!idField) {
|
|
133
|
-
console.error(
|
|
134
|
-
`The id field "${options.idField}" is not present in the schema of the collection "${options.collectionName}".`
|
|
135
|
-
);
|
|
136
|
-
} else if (!VALID_ID_TYPES.includes(idField.type)) {
|
|
137
|
-
console.error(
|
|
138
|
-
`The id field "${options.idField}" for collection "${
|
|
139
|
-
options.collectionName
|
|
140
|
-
}" is of type "${
|
|
141
|
-
idField.type
|
|
142
|
-
}" which is not recommended. Please use one of the following types: ${VALID_ID_TYPES.join(
|
|
143
|
-
", "
|
|
144
|
-
)}.`
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Check if the content field(s) are present
|
|
151
|
-
*/
|
|
152
|
-
function checkContentField(
|
|
153
|
-
fields: Record<string, z.ZodType>,
|
|
154
|
-
options: PocketBaseLoaderOptions
|
|
155
|
-
): void {
|
|
156
|
-
if (
|
|
157
|
-
typeof options.contentFields === "string" &&
|
|
158
|
-
!fields[options.contentFields]
|
|
159
|
-
) {
|
|
160
|
-
console.error(
|
|
161
|
-
`The content field "${options.contentFields}" is not present in the schema of the collection "${options.collectionName}".`
|
|
162
|
-
);
|
|
163
|
-
} else if (Array.isArray(options.contentFields)) {
|
|
164
|
-
for (const field of options.contentFields) {
|
|
165
|
-
if (!fields[field]) {
|
|
166
|
-
console.error(
|
|
167
|
-
`The content field "${field}" is not present in the schema of the collection "${options.collectionName}".`
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Check if the updated field is present
|
|
176
|
-
*/
|
|
177
|
-
function checkUpdatedField(
|
|
178
|
-
fields: Record<string, z.ZodType>,
|
|
179
|
-
collection: PocketBaseCollection,
|
|
180
|
-
options: PocketBaseLoaderOptions
|
|
181
|
-
): void {
|
|
182
|
-
if (!options.updatedField) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!fields[options.updatedField]) {
|
|
187
|
-
console.error(
|
|
188
|
-
`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.`
|
|
189
|
-
);
|
|
190
|
-
} else {
|
|
191
|
-
const updatedField = collection.fields.find(
|
|
192
|
-
(field) => field.name === options.updatedField
|
|
193
|
-
);
|
|
194
|
-
if (updatedField?.type !== "autodate" || !updatedField.onUpdate) {
|
|
195
|
-
console.warn(
|
|
196
|
-
`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!`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ZodObject, ZodPipe } from "astro/zod";
|
|
2
|
-
import {
|
|
3
|
-
createAuxiliaryTypeStore,
|
|
4
|
-
createTypeAlias,
|
|
5
|
-
printNode,
|
|
6
|
-
zodToTs
|
|
7
|
-
} from "zod-to-ts";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Generate a TypeScript type from a Zod schema.
|
|
11
|
-
*/
|
|
12
|
-
export function generateType(schema: ZodObject | ZodPipe): string {
|
|
13
|
-
// If the collection contains file fields, we transform the field values to file urls in a `transform` step.
|
|
14
|
-
// 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.
|
|
15
|
-
const schemaForTypes = schema.type === "pipe" ? schema.in : schema;
|
|
16
|
-
|
|
17
|
-
const { node } = zodToTs(schemaForTypes, {
|
|
18
|
-
auxiliaryTypeStore: createAuxiliaryTypeStore()
|
|
19
|
-
});
|
|
20
|
-
const typeAlias = createTypeAlias(node, "Entry");
|
|
21
|
-
|
|
22
|
-
return `export ${printNode(typeAlias)}`;
|
|
23
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { pocketBaseErrorResponse } from "../types/pocketbase-api-response.type";
|
|
2
|
-
import type { PocketBaseLoaderOptions } from "../types/pocketbase-loader-options.type";
|
|
3
|
-
import type { PocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
4
|
-
import { pocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Fetches the schema for the specified collection from the PocketBase instance.
|
|
8
|
-
*
|
|
9
|
-
* @param options Options for the loader. See {@link PocketBaseLoaderOptions} for more details.
|
|
10
|
-
* @param token The superuser token to authenticate the request.
|
|
11
|
-
*/
|
|
12
|
-
export async function getRemoteSchema(
|
|
13
|
-
options: Pick<PocketBaseLoaderOptions, "collectionName" | "url">,
|
|
14
|
-
token: string
|
|
15
|
-
): Promise<PocketBaseCollection | undefined> {
|
|
16
|
-
// Build URL and headers for the schema request
|
|
17
|
-
const schemaUrl = new URL(
|
|
18
|
-
`api/collections/${options.collectionName}`,
|
|
19
|
-
options.url
|
|
20
|
-
).href;
|
|
21
|
-
const schemaHeaders = new Headers();
|
|
22
|
-
schemaHeaders.set("Authorization", token);
|
|
23
|
-
|
|
24
|
-
// Fetch the schema
|
|
25
|
-
const schemaRequest = await fetch(schemaUrl, {
|
|
26
|
-
headers: schemaHeaders
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// If the request was not successful, try another method
|
|
30
|
-
if (!schemaRequest.ok) {
|
|
31
|
-
const errorResponse = pocketBaseErrorResponse.parse(
|
|
32
|
-
await schemaRequest.json()
|
|
33
|
-
);
|
|
34
|
-
const errorMessage = `Fetching schema from ${options.collectionName} failed with status code ${schemaRequest.status}.\nReason: ${errorResponse.message}`;
|
|
35
|
-
console.error(errorMessage);
|
|
36
|
-
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Get the schema from the response
|
|
41
|
-
const response = pocketBaseCollection.parse(await schemaRequest.json());
|
|
42
|
-
return response;
|
|
43
|
-
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod";
|
|
2
|
-
import type {
|
|
3
|
-
PocketBaseCollection,
|
|
4
|
-
PocketBaseSchemaEntry
|
|
5
|
-
} from "../types/pocketbase-schema.type";
|
|
6
|
-
|
|
7
|
-
export interface ParseSchemaOptions {
|
|
8
|
-
hasSuperuserRights: boolean;
|
|
9
|
-
fieldsToInclude?: Array<string>;
|
|
10
|
-
experimentalLiveTypesOnly?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Converts PocketBase collection fields into Zod types, handling field types, required status, and custom schemas.
|
|
15
|
-
*/
|
|
16
|
-
export function parseSchema(
|
|
17
|
-
collection: PocketBaseCollection,
|
|
18
|
-
customSchemas: Record<string, z.ZodType> | undefined,
|
|
19
|
-
options: ParseSchemaOptions
|
|
20
|
-
): Record<string, z.ZodType> {
|
|
21
|
-
// Prepare the schemas fields
|
|
22
|
-
const fields: Record<string, z.ZodType> = {};
|
|
23
|
-
|
|
24
|
-
// Parse every field in the schema
|
|
25
|
-
for (const field of collection.fields) {
|
|
26
|
-
// If fieldsToInclude is specified, only include fields that are in the list
|
|
27
|
-
if (
|
|
28
|
-
options.fieldsToInclude &&
|
|
29
|
-
!options.fieldsToInclude.includes(field.name)
|
|
30
|
-
) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Skip hidden fields if the user does not have superuser rights
|
|
35
|
-
if (field.hidden && !options.hasSuperuserRights) {
|
|
36
|
-
if (options.fieldsToInclude) {
|
|
37
|
-
console.warn(
|
|
38
|
-
`"${field.name}" is requested but hidden. Provide superuser credentials to include this field.`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let fieldType: z.ZodType;
|
|
46
|
-
|
|
47
|
-
// Determine the field type and create the corresponding Zod type
|
|
48
|
-
switch (field.type) {
|
|
49
|
-
case "number":
|
|
50
|
-
fieldType = z.number();
|
|
51
|
-
break;
|
|
52
|
-
case "bool":
|
|
53
|
-
fieldType = z.boolean();
|
|
54
|
-
break;
|
|
55
|
-
case "date":
|
|
56
|
-
case "autodate":
|
|
57
|
-
if (options.experimentalLiveTypesOnly) {
|
|
58
|
-
// If experimental live types only mode is enabled, treat dates as strings
|
|
59
|
-
fieldType = z.string();
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
// Coerce and parse the value as a date
|
|
63
|
-
fieldType = z.coerce.date();
|
|
64
|
-
break;
|
|
65
|
-
case "geoPoint":
|
|
66
|
-
fieldType = z.object({
|
|
67
|
-
lon: z.number(),
|
|
68
|
-
lat: z.number()
|
|
69
|
-
});
|
|
70
|
-
break;
|
|
71
|
-
case "select": {
|
|
72
|
-
if (!field.values) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Field ${field.name} is of type "select" but has no values defined.`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Create an enum for the select values
|
|
79
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
80
|
-
const values = z.enum(field.values as [string, ...Array<string>]);
|
|
81
|
-
|
|
82
|
-
// Parse the field type based on the number of values it can have
|
|
83
|
-
fieldType = parseSingleOrMultipleValues(field, values);
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
case "relation":
|
|
87
|
-
case "file":
|
|
88
|
-
// NOTE: Relations are currently not supported and are treated as strings
|
|
89
|
-
// NOTE: Files are later transformed to URLs
|
|
90
|
-
|
|
91
|
-
// Parse the field type based on the number of values it can have
|
|
92
|
-
fieldType = parseSingleOrMultipleValues(field, z.string());
|
|
93
|
-
break;
|
|
94
|
-
case "json":
|
|
95
|
-
// Use the user defined custom schema for the field or fallback to unknown
|
|
96
|
-
fieldType = customSchemas?.[field.name] ?? z.unknown();
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
// Default to a string
|
|
100
|
-
fieldType = z.string();
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const isRequired =
|
|
105
|
-
// Check if the field is required
|
|
106
|
-
field.required ||
|
|
107
|
-
// `onCreate autodate` fields are always set
|
|
108
|
-
(field.type === "autodate" && field.onCreate) ||
|
|
109
|
-
// number and bool fields are always set
|
|
110
|
-
field.type === "number" ||
|
|
111
|
-
field.type === "bool";
|
|
112
|
-
|
|
113
|
-
// If the field is not required, mark it as optional
|
|
114
|
-
if (!isRequired) {
|
|
115
|
-
fieldType = z.preprocess(
|
|
116
|
-
(val) => val || undefined,
|
|
117
|
-
z.optional(fieldType)
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Add the field to the fields object
|
|
122
|
-
fields[field.name] = fieldType.meta({
|
|
123
|
-
id: field.id,
|
|
124
|
-
title: field.name,
|
|
125
|
-
description: getFieldDescription(field)
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return fields;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Parse the field type based on the number of values it can have
|
|
134
|
-
*
|
|
135
|
-
* @param field Field to parse
|
|
136
|
-
* @param type Type of each value
|
|
137
|
-
*
|
|
138
|
-
* @returns The parsed field type
|
|
139
|
-
*/
|
|
140
|
-
function parseSingleOrMultipleValues(
|
|
141
|
-
field: PocketBaseSchemaEntry,
|
|
142
|
-
type: z.ZodType
|
|
143
|
-
): z.ZodType {
|
|
144
|
-
// If the select allows multiple values, create an array of the enum
|
|
145
|
-
if (field.maxSelect === undefined || field.maxSelect === 1) {
|
|
146
|
-
return type;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return z.array(type);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get the description for a field based on its help text and type.
|
|
154
|
-
*/
|
|
155
|
-
function getFieldDescription(field: PocketBaseSchemaEntry): string | undefined {
|
|
156
|
-
switch (true) {
|
|
157
|
-
case !!field.help:
|
|
158
|
-
return field.help;
|
|
159
|
-
case field.type === "autodate" && field.onUpdate:
|
|
160
|
-
return "Date when the entry was last updated. This field is automatically updated by PocketBase whenever the entry is updated.";
|
|
161
|
-
case field.type === "autodate" && field.onCreate:
|
|
162
|
-
return "Date when the entry was created. This field is automatically set by PocketBase when the entry is created.";
|
|
163
|
-
case field.name === "id":
|
|
164
|
-
return "The unique identifier for the entry.";
|
|
165
|
-
case field.hidden:
|
|
166
|
-
return "This field is hidden and may require superuser credentials to access.";
|
|
167
|
-
default:
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import fs from "fs/promises";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import type { PocketBaseCollection } from "../types/pocketbase-schema.type";
|
|
4
|
-
import { pocketBaseDatabase } from "../types/pocketbase-schema.type";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Reads the local PocketBase schema file and returns the schema for the specified collection.
|
|
8
|
-
*
|
|
9
|
-
* @param localSchemaPath Path to the local schema file.
|
|
10
|
-
* @param collectionName Name of the collection to get the schema for.
|
|
11
|
-
*/
|
|
12
|
-
export async function readLocalSchema(
|
|
13
|
-
localSchemaPath: string,
|
|
14
|
-
collectionName: string
|
|
15
|
-
): Promise<PocketBaseCollection | undefined> {
|
|
16
|
-
const realPath = path.join(process.cwd(), localSchemaPath);
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Read the schema file
|
|
20
|
-
const schemaFile = await fs.readFile(realPath, "utf-8");
|
|
21
|
-
const fileContent = pocketBaseDatabase.safeParse(JSON.parse(schemaFile));
|
|
22
|
-
|
|
23
|
-
// Check if the database file is valid
|
|
24
|
-
if (!fileContent.success) {
|
|
25
|
-
throw new Error("Invalid schema file");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Find and return the schema for the collection
|
|
29
|
-
const schema = fileContent.data.find(
|
|
30
|
-
(collection) => collection.name === collectionName
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
if (!schema) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
`Collection "${collectionName}" not found in schema file`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return schema;
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error(
|
|
42
|
-
`Failed to read local schema from ${localSchemaPath}. No types will be generated.\nReason: ${error}`
|
|
43
|
-
);
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod";
|
|
2
|
-
import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
|
|
3
|
-
import type { PocketBaseSchemaEntry } from "../types/pocketbase-schema.type";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Transforms file names in a PocketBase entry to file URLs.
|
|
7
|
-
*
|
|
8
|
-
* @param baseUrl URL of the PocketBase instance.
|
|
9
|
-
* @param collection Collection of the entry.
|
|
10
|
-
* @param entry Entry to transform.
|
|
11
|
-
*/
|
|
12
|
-
export function transformFiles(
|
|
13
|
-
baseUrl: string,
|
|
14
|
-
fileFields: Array<PocketBaseSchemaEntry>,
|
|
15
|
-
entry: PocketBaseEntry
|
|
16
|
-
): PocketBaseEntry {
|
|
17
|
-
// Transform all file names to file URLs
|
|
18
|
-
for (const field of fileFields) {
|
|
19
|
-
const fieldName = field.name;
|
|
20
|
-
|
|
21
|
-
if (field.maxSelect === 1) {
|
|
22
|
-
const fileName = z.optional(z.string()).parse(entry[fieldName]);
|
|
23
|
-
// Check if a file name is present
|
|
24
|
-
if (!fileName) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Transform the file name to a file URL
|
|
29
|
-
entry[fieldName] = transformFileUrl(
|
|
30
|
-
baseUrl,
|
|
31
|
-
entry.collectionName,
|
|
32
|
-
entry.id,
|
|
33
|
-
fileName
|
|
34
|
-
);
|
|
35
|
-
} else {
|
|
36
|
-
const fileNames = z.optional(z.array(z.string())).parse(entry[fieldName]);
|
|
37
|
-
// Check if file names are present
|
|
38
|
-
if (!fileNames) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Transform all file names to file URLs
|
|
43
|
-
entry[fieldName] = fileNames.map((file) =>
|
|
44
|
-
transformFileUrl(baseUrl, entry.collectionName, entry.id, file)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return entry;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Transforms a file name to a PocketBase file URL.
|
|
54
|
-
*
|
|
55
|
-
* @param base Base URL of the PocketBase instance.
|
|
56
|
-
* @param collectionName Name of the collection.
|
|
57
|
-
* @param entryId ID of the entry.
|
|
58
|
-
* @param file Name of the file.
|
|
59
|
-
*/
|
|
60
|
-
export function transformFileUrl(
|
|
61
|
-
base: string,
|
|
62
|
-
collectionName: string,
|
|
63
|
-
entryId: string,
|
|
64
|
-
file: string
|
|
65
|
-
): string {
|
|
66
|
-
return new URL(`api/files/${collectionName}/${entryId}/${file}`, base).href;
|
|
67
|
-
}
|
package/src/tsconfig.json
DELETED
package/src/types/errors.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { LiveCollectionError } from "astro/content/runtime";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Error thrown when there is an authentication issue with PocketBase.
|
|
5
|
-
*/
|
|
6
|
-
export class PocketBaseAuthenticationError extends LiveCollectionError {
|
|
7
|
-
constructor(collection: string, message: string) {
|
|
8
|
-
super(collection, message);
|
|
9
|
-
this.name = "PocketBaseAuthenticationError";
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
static is(error: unknown): error is PocketBaseAuthenticationError {
|
|
13
|
-
// This is similar to the original implementation in Astro itself.
|
|
14
|
-
return (
|
|
15
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
16
|
-
!!error && (error as Error).name === "PocketBaseAuthenticationError"
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod";
|
|
2
|
-
import { pocketBaseEntry } from "./pocketbase-entry.type";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The schema for a PocketBase error response.
|
|
6
|
-
*/
|
|
7
|
-
export const pocketBaseErrorResponse = z.object({
|
|
8
|
-
/**
|
|
9
|
-
* The error message returned by PocketBase.
|
|
10
|
-
*/
|
|
11
|
-
message: z.string()
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The schema for a PocketBase list response.
|
|
16
|
-
*/
|
|
17
|
-
export const pocketBaseListResponse = z.object({
|
|
18
|
-
/**
|
|
19
|
-
* Current page number.
|
|
20
|
-
*/
|
|
21
|
-
page: z.number(),
|
|
22
|
-
/**
|
|
23
|
-
* Total number of pages available.
|
|
24
|
-
*/
|
|
25
|
-
totalPages: z.number(),
|
|
26
|
-
/**
|
|
27
|
-
* Array of items in the current page.
|
|
28
|
-
*/
|
|
29
|
-
items: z.array(pocketBaseEntry)
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The schema for a PocketBase login response.
|
|
34
|
-
*/
|
|
35
|
-
export const pocketBaseLoginResponse = z.object({
|
|
36
|
-
/**
|
|
37
|
-
* The authentication token returned by PocketBase.
|
|
38
|
-
*/
|
|
39
|
-
token: z.string()
|
|
40
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Schema for a PocketBase entry.
|
|
5
|
-
*/
|
|
6
|
-
export const pocketBaseEntry = z.looseObject({
|
|
7
|
-
/**
|
|
8
|
-
* ID of the entry.
|
|
9
|
-
*/
|
|
10
|
-
id: z.string(),
|
|
11
|
-
/**
|
|
12
|
-
* ID of the collection the entry belongs to.
|
|
13
|
-
*/
|
|
14
|
-
collectionId: z.string(),
|
|
15
|
-
/**
|
|
16
|
-
* Name of the collection the entry belongs to.
|
|
17
|
-
*/
|
|
18
|
-
collectionName: z.string()
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Type for a PocketBase entry.
|
|
23
|
-
*/
|
|
24
|
-
export type PocketBaseEntry = z.infer<typeof pocketBaseEntry>;
|