@withstudiocms/sdk 0.0.0-beta.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/cache.d.ts +109 -0
- package/dist/cache.js +94 -0
- package/dist/consts.d.ts +37 -0
- package/dist/consts.js +35 -0
- package/dist/context.d.ts +208 -0
- package/dist/context.js +40 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +1024 -0
- package/dist/index.js +24 -0
- package/dist/lib/diff.d.ts +39 -0
- package/dist/lib/diff.js +29 -0
- package/dist/lib/logger.d.ts +31 -0
- package/dist/lib/logger.js +131 -0
- package/dist/lib/pluginUtils.d.ts +222 -0
- package/dist/lib/pluginUtils.js +87 -0
- package/dist/lib/storage-manager.d.ts +10 -0
- package/dist/lib/storage-manager.js +17 -0
- package/dist/migrations/20251025T040912_init.d.ts +17 -0
- package/dist/migrations/20251025T040912_init.js +260 -0
- package/dist/migrations/20251130T150847_drop_deprecated.d.ts +13 -0
- package/dist/migrations/20251130T150847_drop_deprecated.js +262 -0
- package/dist/migrations/20251221T002125_url-mapping.d.ts +13 -0
- package/dist/migrations/20251221T002125_url-mapping.js +228 -0
- package/dist/migrator.d.ts +25 -0
- package/dist/migrator.js +21 -0
- package/dist/modules/auth/index.d.ts +419 -0
- package/dist/modules/auth/index.js +436 -0
- package/dist/modules/clear/index.d.ts +72 -0
- package/dist/modules/clear/index.js +52 -0
- package/dist/modules/config/consts.d.ts +32 -0
- package/dist/modules/config/consts.js +18 -0
- package/dist/modules/config/index.d.ts +100 -0
- package/dist/modules/config/index.js +224 -0
- package/dist/modules/config/templates/mailer.d.ts +36 -0
- package/dist/modules/config/templates/mailer.js +218 -0
- package/dist/modules/config/type-utils.d.ts +13 -0
- package/dist/modules/config/type-utils.js +11 -0
- package/dist/modules/delete/index.d.ts +141 -0
- package/dist/modules/delete/index.js +279 -0
- package/dist/modules/diffTracking/index.d.ts +188 -0
- package/dist/modules/diffTracking/index.js +277 -0
- package/dist/modules/get/index.d.ts +372 -0
- package/dist/modules/get/index.js +579 -0
- package/dist/modules/index.d.ts +883 -0
- package/dist/modules/index.js +37 -0
- package/dist/modules/init/index.d.ts +60 -0
- package/dist/modules/init/index.js +38 -0
- package/dist/modules/middleware/index.d.ts +56 -0
- package/dist/modules/middleware/index.js +50 -0
- package/dist/modules/notificationSettings/index.d.ts +57 -0
- package/dist/modules/notificationSettings/index.js +39 -0
- package/dist/modules/plugins/index.d.ts +167 -0
- package/dist/modules/plugins/index.js +272 -0
- package/dist/modules/post/index.d.ts +306 -0
- package/dist/modules/post/index.js +337 -0
- package/dist/modules/resetTokenBucket/index.d.ts +91 -0
- package/dist/modules/resetTokenBucket/index.js +96 -0
- package/dist/modules/rest_api/index.d.ts +92 -0
- package/dist/modules/rest_api/index.js +117 -0
- package/dist/modules/update/index.d.ts +184 -0
- package/dist/modules/update/index.js +192 -0
- package/dist/modules/util/collectors.d.ts +125 -0
- package/dist/modules/util/collectors.js +168 -0
- package/dist/modules/util/folderTree.d.ts +100 -0
- package/dist/modules/util/folderTree.js +176 -0
- package/dist/modules/util/generators.d.ts +83 -0
- package/dist/modules/util/generators.js +106 -0
- package/dist/modules/util/getFromNPM.d.ts +199 -0
- package/dist/modules/util/getFromNPM.js +106 -0
- package/dist/modules/util/index.d.ts +100 -0
- package/dist/modules/util/index.js +20 -0
- package/dist/modules/util/parsers.d.ts +60 -0
- package/dist/modules/util/parsers.js +43 -0
- package/dist/modules/util/slugify.d.ts +22 -0
- package/dist/modules/util/slugify.js +19 -0
- package/dist/modules/util/users.d.ts +99 -0
- package/dist/modules/util/users.js +78 -0
- package/dist/tables.d.ts +433 -0
- package/dist/tables.js +169 -0
- package/dist/types.d.ts +359 -0
- package/dist/types.js +10 -0
- package/package.json +67 -7
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Effect, Schema } from "@withstudiocms/effect";
|
|
2
|
+
import { DBClientLive } from "../../context.js";
|
|
3
|
+
import { StudioCMSAPIKeys, StudioCMSPermissions } from "../../tables.js";
|
|
4
|
+
import { SDKGenerators } from "../util/generators.js";
|
|
5
|
+
const SDKRestAPIModule = Effect.gen(function* () {
|
|
6
|
+
const [{ withCodec, withEncoder }, { generateToken }] = yield* Effect.all([
|
|
7
|
+
DBClientLive,
|
|
8
|
+
SDKGenerators
|
|
9
|
+
]);
|
|
10
|
+
const _getTokensForUser = withCodec({
|
|
11
|
+
encoder: Schema.String,
|
|
12
|
+
decoder: Schema.Array(StudioCMSAPIKeys.Select),
|
|
13
|
+
callbackFn: (db, userId) => db(
|
|
14
|
+
(client) => client.selectFrom("StudioCMSAPIKeys").selectAll().where("userId", "=", userId).execute()
|
|
15
|
+
)
|
|
16
|
+
});
|
|
17
|
+
const _newToken = withCodec({
|
|
18
|
+
encoder: StudioCMSAPIKeys.Insert,
|
|
19
|
+
decoder: StudioCMSAPIKeys.Select,
|
|
20
|
+
callbackFn: (db, tokenData) => db(
|
|
21
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
22
|
+
await trx.insertInto("StudioCMSAPIKeys").values(tokenData).executeTakeFirstOrThrow();
|
|
23
|
+
return await trx.selectFrom("StudioCMSAPIKeys").selectAll().where("id", "=", tokenData.id).executeTakeFirstOrThrow();
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
});
|
|
27
|
+
const _deleteToken = withEncoder({
|
|
28
|
+
encoder: Schema.Struct({
|
|
29
|
+
userId: Schema.String,
|
|
30
|
+
tokenId: Schema.String
|
|
31
|
+
}),
|
|
32
|
+
callbackFn: (db, { userId, tokenId }) => db(
|
|
33
|
+
(client) => client.deleteFrom("StudioCMSAPIKeys").where("userId", "=", userId).where("id", "=", tokenId).execute()
|
|
34
|
+
)
|
|
35
|
+
});
|
|
36
|
+
const _getByKey = withCodec({
|
|
37
|
+
encoder: Schema.String,
|
|
38
|
+
decoder: Schema.UndefinedOr(StudioCMSAPIKeys.Select),
|
|
39
|
+
callbackFn: (db, key) => db(
|
|
40
|
+
(client) => client.selectFrom("StudioCMSAPIKeys").selectAll().where("key", "=", key).executeTakeFirst()
|
|
41
|
+
)
|
|
42
|
+
});
|
|
43
|
+
const _getKeyPermissions = withCodec({
|
|
44
|
+
encoder: Schema.String,
|
|
45
|
+
decoder: Schema.UndefinedOr(StudioCMSPermissions.Select),
|
|
46
|
+
callbackFn: (db, userId) => db(
|
|
47
|
+
(client) => client.selectFrom("StudioCMSPermissions").selectAll().where("user", "=", userId).executeTakeFirst()
|
|
48
|
+
)
|
|
49
|
+
});
|
|
50
|
+
const _createNewTokenForUser = Effect.fn(
|
|
51
|
+
(userId, description) => generateToken(userId, true).pipe(
|
|
52
|
+
Effect.flatMap(
|
|
53
|
+
(key) => _newToken({
|
|
54
|
+
id: crypto.randomUUID(),
|
|
55
|
+
key,
|
|
56
|
+
userId,
|
|
57
|
+
description,
|
|
58
|
+
creationDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
59
|
+
})
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
const _verifyToken = Effect.fn(
|
|
64
|
+
(key) => Effect.gen(function* () {
|
|
65
|
+
const apiKeyRecord = yield* _getByKey(key);
|
|
66
|
+
if (!apiKeyRecord) return false;
|
|
67
|
+
const permissionsRecord = yield* _getKeyPermissions(apiKeyRecord.userId);
|
|
68
|
+
if (!permissionsRecord) return false;
|
|
69
|
+
return {
|
|
70
|
+
userId: apiKeyRecord.userId,
|
|
71
|
+
key: apiKeyRecord.key,
|
|
72
|
+
rank: permissionsRecord.rank
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
const tokens = {
|
|
77
|
+
/**
|
|
78
|
+
* Retrieves all API tokens for a specific user.
|
|
79
|
+
*
|
|
80
|
+
* @param userId - The ID of the user whose tokens are to be retrieved.
|
|
81
|
+
* @returns An Effect that resolves to an array of API keys for the user.
|
|
82
|
+
* @throws {LibSQLDatabaseError} If a database error occurs during the operation.
|
|
83
|
+
*/
|
|
84
|
+
get: _getTokensForUser,
|
|
85
|
+
/**
|
|
86
|
+
* Creates a new API token for a user with the specified description.
|
|
87
|
+
*
|
|
88
|
+
* @param userId - The ID of the user for whom to create the token.
|
|
89
|
+
* @param description - A description for the API key.
|
|
90
|
+
* @returns An Effect that resolves to the created API key record.
|
|
91
|
+
* @throws {LibSQLDatabaseError} If a database error occurs during the operation.
|
|
92
|
+
*/
|
|
93
|
+
new: _createNewTokenForUser,
|
|
94
|
+
/**
|
|
95
|
+
* Deletes an API token for a user by its ID.
|
|
96
|
+
*
|
|
97
|
+
* @param userId - The ID of the user whose token is to be deleted.
|
|
98
|
+
* @param tokenId - The ID of the API token to delete.
|
|
99
|
+
* @returns An Effect that resolves when the token is successfully deleted.
|
|
100
|
+
* @throws {LibSQLDatabaseError} If a database error occurs during the operation.
|
|
101
|
+
*/
|
|
102
|
+
delete: _deleteToken,
|
|
103
|
+
/**
|
|
104
|
+
* Verifies an API token and retrieves associated user information.
|
|
105
|
+
*
|
|
106
|
+
* @param key - The API token to verify.
|
|
107
|
+
* @returns An Effect that resolves to user information if the token is valid, or false if invalid.
|
|
108
|
+
*/
|
|
109
|
+
verify: _verifyToken
|
|
110
|
+
};
|
|
111
|
+
return { tokens };
|
|
112
|
+
});
|
|
113
|
+
var rest_api_default = SDKRestAPIModule;
|
|
114
|
+
export {
|
|
115
|
+
SDKRestAPIModule,
|
|
116
|
+
rest_api_default as default
|
|
117
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Effect } from '@withstudiocms/effect';
|
|
2
|
+
import CacheService from '../../cache.js';
|
|
3
|
+
import { DBClientLive } from '../../context.js';
|
|
4
|
+
import { StudioCMSPageContent, StudioCMSPageData } from '../../tables.js';
|
|
5
|
+
/**
|
|
6
|
+
* CombinedPageUpdateData
|
|
7
|
+
*
|
|
8
|
+
* Type representing the combined data required to update both page data and page content.
|
|
9
|
+
*/
|
|
10
|
+
type CombinedPageUpdateData = {
|
|
11
|
+
pageData: (typeof StudioCMSPageData.Update)['Type'];
|
|
12
|
+
pageContent: (typeof StudioCMSPageContent.Update)['Type'];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* SDKUpdateModule
|
|
16
|
+
*
|
|
17
|
+
* Effect generator that constructs and returns the SDK "UPDATE" module containing all update-related operations
|
|
18
|
+
* for the StudioCMS domain. Each operation is implemented as an Effect and typically combines:
|
|
19
|
+
* - a DB update operation (wrapped with a codec for runtime encode/decode/validation),
|
|
20
|
+
* - optional cache invalidation or cache refresh,
|
|
21
|
+
* - and retrieval of the fresh resource via the GET module when appropriate.
|
|
22
|
+
*
|
|
23
|
+
* @module SDKUpdateModule
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* - Dependencies resolved by the generator: DBClientLive, CacheService, SDKClearModule, SDKGetModule, SDKConfigModule.
|
|
27
|
+
* - DB operations are created via withCodec with corresponding encoder/decoder codecs (e.g. StudioCMSPageContent, StudioCMSPageData, StudioCMSPermissions, etc.).
|
|
28
|
+
* - Cache-related operations include clearing/invalidation of folder tree/list caches, page-specific cache deletion, and npm package cache tag invalidation.
|
|
29
|
+
* - Composite helpers coordinate multi-step flows, e.g. updating page content + page data + deleting the page cache, then returning the fresh page via GET.
|
|
30
|
+
* - All operations are effectful and intended to be executed inside the Effect runtime; callers should compose and run them using the Effect primitives provided by the environment.
|
|
31
|
+
*
|
|
32
|
+
* @returns {{
|
|
33
|
+
* pageContent: Effect, tags: Effect, categories: Effect, permissions: Effect,
|
|
34
|
+
* folderTree: Effect, folderList: Effect, folder: Effect,
|
|
35
|
+
* latestVersion: Effect, siteConfig: Effect,
|
|
36
|
+
* page: { byId: Effect, bySlug: Effect }
|
|
37
|
+
* }}
|
|
38
|
+
* An object exposing update operations:
|
|
39
|
+
* - pageContent, tags, categories, permissions: DB update Effects returning the updated record.
|
|
40
|
+
* - folderTree, folderList: Effects that refresh corresponding caches.
|
|
41
|
+
* - folder: updates a folder entry and invalidates/refreshes folder caches.
|
|
42
|
+
* - latestVersion: invalidates npm package cache tags and fetches the latest package version.
|
|
43
|
+
* - siteConfig: forwards to CONFIG.siteConfig.update.
|
|
44
|
+
* - page.byId: updates page data and content, deletes the page cache, refreshes folder caches, then returns the updated page.
|
|
45
|
+
* - page.bySlug: resolves the page id by slug and delegates to page.byId.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Typical usage inside an Effect:
|
|
49
|
+
* // yield* Effect.flatMap(UPDATE.page.byId('pageId', combinedPageUpdateData))
|
|
50
|
+
*
|
|
51
|
+
* @threadSafety
|
|
52
|
+
* Effects encapsulate async side effects; callers should treat composite updates as atomic logical operations but not assume cross-operation DB transactions unless provided by the DB client.
|
|
53
|
+
*
|
|
54
|
+
* @errors
|
|
55
|
+
* - DB client errors (e.g. executeTakeFirstOrThrow) propagate as Effect failures.
|
|
56
|
+
* - Codec validation errors are raised if supplied data does not conform to the expected codec schemas.
|
|
57
|
+
*
|
|
58
|
+
* @see SDKGetModule for read operations
|
|
59
|
+
* @see SDKClearModule for explicit cache clearing operations
|
|
60
|
+
*/
|
|
61
|
+
export declare const SDKUpdateModule: Effect.Effect<{
|
|
62
|
+
/**
|
|
63
|
+
* Update Page Content
|
|
64
|
+
*
|
|
65
|
+
* @param data - The page content data to update.
|
|
66
|
+
* @returns The updated page content.
|
|
67
|
+
*/
|
|
68
|
+
pageContent: (input: {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
readonly contentLang: string;
|
|
71
|
+
readonly contentId: string;
|
|
72
|
+
readonly content: string;
|
|
73
|
+
}) => Effect.Effect<{
|
|
74
|
+
readonly id: string;
|
|
75
|
+
readonly contentLang: string;
|
|
76
|
+
readonly contentId: string;
|
|
77
|
+
readonly content: string;
|
|
78
|
+
}, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
79
|
+
/**
|
|
80
|
+
* Update Tag
|
|
81
|
+
*
|
|
82
|
+
* @param data - The tag data to update.
|
|
83
|
+
* @returns The updated tag.
|
|
84
|
+
*/
|
|
85
|
+
tags: (input: {
|
|
86
|
+
readonly id: number;
|
|
87
|
+
readonly name: string;
|
|
88
|
+
readonly description: string;
|
|
89
|
+
readonly slug: string;
|
|
90
|
+
readonly meta: string;
|
|
91
|
+
}) => Effect.Effect<{
|
|
92
|
+
readonly id: number;
|
|
93
|
+
readonly name: string;
|
|
94
|
+
readonly description: string;
|
|
95
|
+
readonly slug: string;
|
|
96
|
+
readonly meta: {
|
|
97
|
+
readonly [x: string]: unknown;
|
|
98
|
+
};
|
|
99
|
+
}, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
100
|
+
/**
|
|
101
|
+
* Update Category
|
|
102
|
+
*
|
|
103
|
+
* @param data - The category data to update.
|
|
104
|
+
* @returns The updated category.
|
|
105
|
+
*/
|
|
106
|
+
categories: (input: {
|
|
107
|
+
readonly id: number;
|
|
108
|
+
readonly name: string;
|
|
109
|
+
readonly description: string;
|
|
110
|
+
readonly parent?: number | null | undefined;
|
|
111
|
+
readonly slug: string;
|
|
112
|
+
readonly meta: string;
|
|
113
|
+
}) => Effect.Effect<{
|
|
114
|
+
readonly id: number;
|
|
115
|
+
readonly name: string;
|
|
116
|
+
readonly description: string;
|
|
117
|
+
readonly parent?: number | null | undefined;
|
|
118
|
+
readonly slug: string;
|
|
119
|
+
readonly meta: {
|
|
120
|
+
readonly [x: string]: unknown;
|
|
121
|
+
};
|
|
122
|
+
}, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
123
|
+
/**
|
|
124
|
+
* Update Permissions
|
|
125
|
+
*
|
|
126
|
+
* @param data - The permissions data to update.
|
|
127
|
+
* @returns The updated permissions.
|
|
128
|
+
*/
|
|
129
|
+
permissions: (input: {
|
|
130
|
+
readonly user: string;
|
|
131
|
+
readonly rank: "owner" | "admin" | "editor" | "visitor" | "unknown";
|
|
132
|
+
}) => Effect.Effect<{
|
|
133
|
+
readonly user: string;
|
|
134
|
+
readonly rank: "owner" | "admin" | "editor" | "visitor" | "unknown";
|
|
135
|
+
}, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
136
|
+
/**
|
|
137
|
+
* Update Folder Tree Cache
|
|
138
|
+
*/
|
|
139
|
+
folderTree: Effect.Effect<import("../../types.js").FolderNode[], import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError | import("../util/folderTree.js").FolderTreeError, never>;
|
|
140
|
+
/**
|
|
141
|
+
* Update Folder List Cache
|
|
142
|
+
*/
|
|
143
|
+
folderList: Effect.Effect<import("../../types.js").FolderListItem[], import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
144
|
+
/**
|
|
145
|
+
* Update Folder Entry and Invalidate Related Caches
|
|
146
|
+
*
|
|
147
|
+
* @param data - The folder entry data to update.
|
|
148
|
+
* @returns The updated folder entry.
|
|
149
|
+
*/
|
|
150
|
+
folder: (data: {
|
|
151
|
+
readonly id: string;
|
|
152
|
+
readonly name: string;
|
|
153
|
+
readonly parent?: string | null | undefined;
|
|
154
|
+
}) => Effect.Effect<{
|
|
155
|
+
readonly id: string;
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly parent?: string | null | undefined;
|
|
158
|
+
}, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError | import("../util/folderTree.js").FolderTreeError, never>;
|
|
159
|
+
/**
|
|
160
|
+
* Update Latest NPM Package Version
|
|
161
|
+
*/
|
|
162
|
+
latestVersion: () => Effect.Effect<{
|
|
163
|
+
version: string;
|
|
164
|
+
lastCacheUpdate: Date;
|
|
165
|
+
}, import("effect/Cause").UnknownException | import("effect/ParseResult").ParseError | import("../util/getFromNPM.js").GetFromNPMError, never>;
|
|
166
|
+
/**
|
|
167
|
+
* Update Site Configuration
|
|
168
|
+
*/
|
|
169
|
+
siteConfig: (data: import("../../types.js").ConfigFinal<import("../../types.js").StudioCMSSiteConfig>) => Effect.Effect<import("../../types.js").DynamicConfigEntry<import("../../types.js").StudioCMSSiteConfig>, import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError, never>;
|
|
170
|
+
/**
|
|
171
|
+
* Page Operations
|
|
172
|
+
*/
|
|
173
|
+
page: {
|
|
174
|
+
/**
|
|
175
|
+
* Update Page by ID
|
|
176
|
+
*/
|
|
177
|
+
byId: (pageId: string, data: CombinedPageUpdateData) => Effect.Effect<import("../../types.js").CombinedPageData | undefined, import("effect/ParseResult").ParseError | import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").QueryParseError | import("@withstudiocms/kysely/core/errors").QueryError | import("@withstudiocms/kysely/core/errors").NotFoundError | import("../util/folderTree.js").FolderTreeError | import("../util/collectors.js").CollectorError | import("../get/index.js").PaginateError, never>;
|
|
178
|
+
/**
|
|
179
|
+
* Update Page by Slug
|
|
180
|
+
*/
|
|
181
|
+
bySlug: (slug: string, data: CombinedPageUpdateData) => Effect.Effect<import("../../types.js").CombinedPageData | undefined, import("effect/ParseResult").ParseError | import("@withstudiocms/kysely/client").DBCallbackFailure | import("@withstudiocms/kysely/core/errors").DatabaseError | import("../util/folderTree.js").FolderTreeError | import("../util/collectors.js").CollectorError | import("../get/index.js").PaginateError, never>;
|
|
182
|
+
};
|
|
183
|
+
}, never, DBClientLive | import("../../context.js").SDKDefaults | import("../../context.js").StorageManagerResolver | CacheService | import("@withstudiocms/effect").Deepmerge>;
|
|
184
|
+
export default SDKUpdateModule;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Effect, Schema } from "@withstudiocms/effect";
|
|
2
|
+
import CacheService from "../../cache.js";
|
|
3
|
+
import { cacheKeyGetters, cacheTags } from "../../consts.js";
|
|
4
|
+
import { DBClientLive } from "../../context.js";
|
|
5
|
+
import {
|
|
6
|
+
StudioCMSPageContent,
|
|
7
|
+
StudioCMSPageData,
|
|
8
|
+
StudioCMSPageDataCategories,
|
|
9
|
+
StudioCMSPageDataTags,
|
|
10
|
+
StudioCMSPageFolderStructure,
|
|
11
|
+
StudioCMSPermissions
|
|
12
|
+
} from "../../tables.js";
|
|
13
|
+
import SDKClearModule from "../clear/index.js";
|
|
14
|
+
import SDKConfigModule from "../config/index.js";
|
|
15
|
+
import SDKGetModule from "../get/index.js";
|
|
16
|
+
const SDKUpdateModule = Effect.gen(function* () {
|
|
17
|
+
const [{ withCodec }, CACHE, CLEAR, GET, CONFIG] = yield* Effect.all([
|
|
18
|
+
DBClientLive,
|
|
19
|
+
CacheService,
|
|
20
|
+
SDKClearModule,
|
|
21
|
+
SDKGetModule,
|
|
22
|
+
SDKConfigModule
|
|
23
|
+
]);
|
|
24
|
+
const _updatePageContent = withCodec({
|
|
25
|
+
encoder: StudioCMSPageContent.Update,
|
|
26
|
+
decoder: StudioCMSPageContent.Select,
|
|
27
|
+
callbackFn: (db, data) => db(
|
|
28
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
29
|
+
await trx.updateTable("StudioCMSPageContent").set(data).where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
30
|
+
return await trx.selectFrom("StudioCMSPageContent").selectAll().where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
});
|
|
34
|
+
const _updateTag = withCodec({
|
|
35
|
+
encoder: StudioCMSPageDataTags.Update,
|
|
36
|
+
decoder: StudioCMSPageDataTags.Select,
|
|
37
|
+
callbackFn: (db, data) => db(
|
|
38
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
39
|
+
await trx.updateTable("StudioCMSPageDataTags").set(data).where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
40
|
+
return await trx.selectFrom("StudioCMSPageDataTags").selectAll().where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
});
|
|
44
|
+
const _updateCategory = withCodec({
|
|
45
|
+
encoder: StudioCMSPageDataCategories.Update,
|
|
46
|
+
decoder: StudioCMSPageDataCategories.Select,
|
|
47
|
+
callbackFn: (db, data) => db(
|
|
48
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
49
|
+
await trx.updateTable("StudioCMSPageDataCategories").set(data).where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
50
|
+
return await trx.selectFrom("StudioCMSPageDataCategories").selectAll().where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
});
|
|
54
|
+
const _updatePermission = withCodec({
|
|
55
|
+
encoder: StudioCMSPermissions.Update,
|
|
56
|
+
decoder: StudioCMSPermissions.Select,
|
|
57
|
+
callbackFn: (db, data) => db(
|
|
58
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
59
|
+
await trx.updateTable("StudioCMSPermissions").set(data).where("user", "=", data.user).executeTakeFirstOrThrow();
|
|
60
|
+
return await trx.selectFrom("StudioCMSPermissions").selectAll().where("user", "=", data.user).executeTakeFirstOrThrow();
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
});
|
|
64
|
+
const _updateFolderEntry = withCodec({
|
|
65
|
+
encoder: StudioCMSPageFolderStructure.Update,
|
|
66
|
+
decoder: StudioCMSPageFolderStructure.Select,
|
|
67
|
+
callbackFn: (db, data) => db(
|
|
68
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
69
|
+
await trx.updateTable("StudioCMSPageFolderStructure").set(data).where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
70
|
+
return await trx.selectFrom("StudioCMSPageFolderStructure").selectAll().where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
71
|
+
})
|
|
72
|
+
)
|
|
73
|
+
});
|
|
74
|
+
const _updatePageDataEntry = withCodec({
|
|
75
|
+
encoder: StudioCMSPageData.Update,
|
|
76
|
+
decoder: StudioCMSPageData.Select,
|
|
77
|
+
callbackFn: (db, data) => db(
|
|
78
|
+
(client) => client.transaction().execute(async (trx) => {
|
|
79
|
+
await trx.updateTable("StudioCMSPageData").set(data).where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
80
|
+
return await trx.selectFrom("StudioCMSPageData").selectAll().where("id", "=", data.id).executeTakeFirstOrThrow();
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
});
|
|
84
|
+
const _findPageDataBySlug = withCodec({
|
|
85
|
+
encoder: Schema.String,
|
|
86
|
+
decoder: StudioCMSPageData.Select,
|
|
87
|
+
callbackFn: (db, slug) => db(
|
|
88
|
+
(client) => client.selectFrom("StudioCMSPageData").where("slug", "=", slug).selectAll().executeTakeFirstOrThrow()
|
|
89
|
+
)
|
|
90
|
+
});
|
|
91
|
+
const _updateFolderTree = CLEAR.folderTree.pipe(Effect.flatMap(GET.folderTree));
|
|
92
|
+
const _updateFolderList = CLEAR.folderList.pipe(Effect.flatMap(GET.folderList));
|
|
93
|
+
const _updateFolderEntryAndInvalidate = Effect.fn(
|
|
94
|
+
(data) => _updateFolderEntry(data).pipe(
|
|
95
|
+
Effect.flatMap((src) => _updateFolderTree.pipe(Effect.as(src))),
|
|
96
|
+
Effect.flatMap((src) => _updateFolderList.pipe(Effect.as(src)))
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
const _updateLatestVersion = Effect.fn(
|
|
100
|
+
() => CACHE.invalidateTags(cacheTags.npmPackage).pipe(Effect.flatMap(GET.latestVersion))
|
|
101
|
+
);
|
|
102
|
+
const _updateFolderTreeAndList = Effect.all([_updateFolderTree, _updateFolderList]);
|
|
103
|
+
const _updatePageById = Effect.fn(
|
|
104
|
+
(pageId, data) => Effect.all([
|
|
105
|
+
_updatePageDataEntry(data.pageData),
|
|
106
|
+
_updatePageContent(data.pageContent),
|
|
107
|
+
CACHE.delete(cacheKeyGetters.page(pageId))
|
|
108
|
+
]).pipe(
|
|
109
|
+
Effect.tap(() => _updateFolderTreeAndList),
|
|
110
|
+
Effect.flatMap(() => GET.page.byId(pageId))
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
const _findPageIdBySlug = Effect.fn(
|
|
114
|
+
(slug) => _findPageDataBySlug(slug).pipe(Effect.map(({ id }) => id))
|
|
115
|
+
);
|
|
116
|
+
const _updatePageByIdPiped = (data) => Effect.fn((id) => _updatePageById(id, data));
|
|
117
|
+
const _updatePageBySlug = Effect.fn(
|
|
118
|
+
(slug, data) => _findPageIdBySlug(slug).pipe(Effect.flatMap(_updatePageByIdPiped(data)))
|
|
119
|
+
);
|
|
120
|
+
const UPDATE = {
|
|
121
|
+
/**
|
|
122
|
+
* Update Page Content
|
|
123
|
+
*
|
|
124
|
+
* @param data - The page content data to update.
|
|
125
|
+
* @returns The updated page content.
|
|
126
|
+
*/
|
|
127
|
+
pageContent: _updatePageContent,
|
|
128
|
+
/**
|
|
129
|
+
* Update Tag
|
|
130
|
+
*
|
|
131
|
+
* @param data - The tag data to update.
|
|
132
|
+
* @returns The updated tag.
|
|
133
|
+
*/
|
|
134
|
+
tags: _updateTag,
|
|
135
|
+
/**
|
|
136
|
+
* Update Category
|
|
137
|
+
*
|
|
138
|
+
* @param data - The category data to update.
|
|
139
|
+
* @returns The updated category.
|
|
140
|
+
*/
|
|
141
|
+
categories: _updateCategory,
|
|
142
|
+
/**
|
|
143
|
+
* Update Permissions
|
|
144
|
+
*
|
|
145
|
+
* @param data - The permissions data to update.
|
|
146
|
+
* @returns The updated permissions.
|
|
147
|
+
*/
|
|
148
|
+
permissions: _updatePermission,
|
|
149
|
+
/**
|
|
150
|
+
* Update Folder Tree Cache
|
|
151
|
+
*/
|
|
152
|
+
folderTree: _updateFolderTree,
|
|
153
|
+
/**
|
|
154
|
+
* Update Folder List Cache
|
|
155
|
+
*/
|
|
156
|
+
folderList: _updateFolderList,
|
|
157
|
+
/**
|
|
158
|
+
* Update Folder Entry and Invalidate Related Caches
|
|
159
|
+
*
|
|
160
|
+
* @param data - The folder entry data to update.
|
|
161
|
+
* @returns The updated folder entry.
|
|
162
|
+
*/
|
|
163
|
+
folder: _updateFolderEntryAndInvalidate,
|
|
164
|
+
/**
|
|
165
|
+
* Update Latest NPM Package Version
|
|
166
|
+
*/
|
|
167
|
+
latestVersion: _updateLatestVersion,
|
|
168
|
+
/**
|
|
169
|
+
* Update Site Configuration
|
|
170
|
+
*/
|
|
171
|
+
siteConfig: CONFIG.siteConfig.update,
|
|
172
|
+
/**
|
|
173
|
+
* Page Operations
|
|
174
|
+
*/
|
|
175
|
+
page: {
|
|
176
|
+
/**
|
|
177
|
+
* Update Page by ID
|
|
178
|
+
*/
|
|
179
|
+
byId: _updatePageById,
|
|
180
|
+
/**
|
|
181
|
+
* Update Page by Slug
|
|
182
|
+
*/
|
|
183
|
+
bySlug: _updatePageBySlug
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
return UPDATE;
|
|
187
|
+
});
|
|
188
|
+
var update_default = SDKUpdateModule;
|
|
189
|
+
export {
|
|
190
|
+
SDKUpdateModule,
|
|
191
|
+
update_default as default
|
|
192
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Effect, type ParseResult } from '@withstudiocms/effect';
|
|
2
|
+
import type { DBCallbackFailure } from '@withstudiocms/kysely/client';
|
|
3
|
+
import type { DatabaseError } from '@withstudiocms/kysely/core/errors';
|
|
4
|
+
import { DBClientLive, StorageManagerResolver } from '../../context.js';
|
|
5
|
+
import type { CombinedPageData, CombinedUserData, FolderNode, MetaOnlyPageData, tsPageDataSelect } from '../../types.js';
|
|
6
|
+
import { type FolderTreeError } from './folderTree.js';
|
|
7
|
+
declare const CollectorError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
8
|
+
readonly _tag: "CollectorError";
|
|
9
|
+
} & Readonly<A>;
|
|
10
|
+
/**
|
|
11
|
+
* Error class for collector errors.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CollectorError extends CollectorError_base<{
|
|
14
|
+
cause: unknown;
|
|
15
|
+
}> {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Utility function to handle errors in collector functions.
|
|
19
|
+
*
|
|
20
|
+
* @param _try - The function to execute that may throw an error.
|
|
21
|
+
* @returns An effect that either yields the result of the function or a CollectorError.
|
|
22
|
+
*/
|
|
23
|
+
export declare const useCollectorError: <T>(_try: () => T) => Effect.Effect<T, CollectorError, never>;
|
|
24
|
+
/**
|
|
25
|
+
* SDKCollectors
|
|
26
|
+
*
|
|
27
|
+
* Effect generator that wires together database access, folder-tree utilities, and parsing helpers
|
|
28
|
+
* to produce a set of high-level "collector" utilities for assembling richer SDK models from
|
|
29
|
+
* raw database rows.
|
|
30
|
+
*
|
|
31
|
+
* Behavior
|
|
32
|
+
* - Instantiates required dependencies (DB client, folder-tree helpers, parsers) by yielding the
|
|
33
|
+
* corresponding live implementations.
|
|
34
|
+
* - Exposes a small collection of helper effects and functions that perform typed queries,
|
|
35
|
+
* transform results, and compose several queries into complete domain objects.
|
|
36
|
+
*
|
|
37
|
+
* Internal helpers (provided inside the effect)
|
|
38
|
+
* - _getUserData(id: string): Effect that queries StudioCMSUsersTable and decodes the result.
|
|
39
|
+
* - _getPageContent(id: string): Effect that queries StudioCMSPageContent for multi-language page
|
|
40
|
+
* content and decodes the result.
|
|
41
|
+
* - _getOAuthAccountData(id: string): Effect that queries StudioCMSOAuthAccounts for a user's
|
|
42
|
+
* OAuth accounts and decodes the result.
|
|
43
|
+
* - _getUserPermissionsData(id: string): Effect that queries StudioCMSPermissions for a user's
|
|
44
|
+
* permissions and decodes the result.
|
|
45
|
+
*
|
|
46
|
+
* Utility functions
|
|
47
|
+
* - _transformPageDataToMetaOnly:
|
|
48
|
+
* Transforms a CombinedPageData (or array thereof) into its metadata-only representation by
|
|
49
|
+
* stripping large content fields (defaultContent, multiLangContent). Returns an Effect that
|
|
50
|
+
* fails with a CollectorError when transformation fails.
|
|
51
|
+
* - _collectContributorData(ids: readonly string[]):
|
|
52
|
+
* Concurrently fetches user records for a list of contributor IDs and filters out missing
|
|
53
|
+
* results.
|
|
54
|
+
*
|
|
55
|
+
* Main collectors (returned object)
|
|
56
|
+
* - collectCategories(ids: number[]):
|
|
57
|
+
* Uses a codec-backed query to fetch category rows by id.
|
|
58
|
+
* - collectTags(ids: number[]):
|
|
59
|
+
* Uses a codec-backed query to fetch tag rows by id.
|
|
60
|
+
* - collectPageData(page, tree, metaOnly = false):
|
|
61
|
+
* Assembles a complete page model by concurrently collecting:
|
|
62
|
+
* - categories and tags (via parsers -> collectCategories/collectTags),
|
|
63
|
+
* - contributor user data,
|
|
64
|
+
* - author user data,
|
|
65
|
+
* - multi-language page content (when metaOnly is false).
|
|
66
|
+
* It computes a safe slug (special-casing "index" -> "/") and resolves the full URL
|
|
67
|
+
* route by walking the provided folder tree (using findNodesAlongPathToId) when a
|
|
68
|
+
* parentFolder is present.
|
|
69
|
+
* Overloads:
|
|
70
|
+
* - Without metaOnly (or metaOnly === false) returns CombinedPageData.
|
|
71
|
+
* - With metaOnly === true returns MetaOnlyPageData (content stripped via
|
|
72
|
+
* _transformPageDataToMetaOnly).
|
|
73
|
+
* Possible failure modes include DatabaseError, ParseResult.ParseError, FolderTreeError,
|
|
74
|
+
* and CollectorError.
|
|
75
|
+
* - collectUserData(user):
|
|
76
|
+
* Enriches a user row with its OAuth accounts and permissions and returns a CombinedUserData
|
|
77
|
+
* result.
|
|
78
|
+
*
|
|
79
|
+
* Errors
|
|
80
|
+
* - All operations are represented as Effects and may fail with the module's domain errors such
|
|
81
|
+
* as DatabaseError, ParseResult.ParseError, FolderTreeError, and CollectorError.
|
|
82
|
+
*
|
|
83
|
+
* Usage
|
|
84
|
+
* - The effect yields an object with the above collector functions which can be used by other
|
|
85
|
+
* SDK modules to obtain normalized, assembled data for pages, users, tags, and categories.
|
|
86
|
+
*/
|
|
87
|
+
export declare const SDKCollectors: Effect.Effect<{
|
|
88
|
+
collectCategories: (input: readonly number[]) => Effect.Effect<readonly {
|
|
89
|
+
readonly id: number;
|
|
90
|
+
readonly name: string;
|
|
91
|
+
readonly description: string;
|
|
92
|
+
readonly parent?: number | null | undefined;
|
|
93
|
+
readonly slug: string;
|
|
94
|
+
readonly meta: {
|
|
95
|
+
readonly [x: string]: unknown;
|
|
96
|
+
};
|
|
97
|
+
}[], DBCallbackFailure | DatabaseError, never>;
|
|
98
|
+
collectTags: (input: readonly number[]) => Effect.Effect<readonly {
|
|
99
|
+
readonly id: number;
|
|
100
|
+
readonly name: string;
|
|
101
|
+
readonly description: string;
|
|
102
|
+
readonly slug: string;
|
|
103
|
+
readonly meta: {
|
|
104
|
+
readonly [x: string]: unknown;
|
|
105
|
+
};
|
|
106
|
+
}[], DBCallbackFailure | DatabaseError, never>;
|
|
107
|
+
collectPageData: {
|
|
108
|
+
(page: tsPageDataSelect, tree: FolderNode[]): Effect.Effect<CombinedPageData, CollectorError | FolderTreeError | DBCallbackFailure | DatabaseError | ParseResult.ParseError, never>;
|
|
109
|
+
(page: tsPageDataSelect, tree: FolderNode[], metaOnly: boolean): Effect.Effect<MetaOnlyPageData, CollectorError | FolderTreeError | DBCallbackFailure | DatabaseError | ParseResult.ParseError, never>;
|
|
110
|
+
};
|
|
111
|
+
collectUserData: (user: {
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly url?: string | null | undefined;
|
|
114
|
+
readonly name: string;
|
|
115
|
+
readonly email?: string | null | undefined;
|
|
116
|
+
readonly avatar?: string | null | undefined;
|
|
117
|
+
readonly username: string;
|
|
118
|
+
readonly password?: string | null | undefined;
|
|
119
|
+
readonly updatedAt: Date;
|
|
120
|
+
readonly createdAt: Date;
|
|
121
|
+
readonly emailVerified: boolean;
|
|
122
|
+
readonly notifications?: string | null | undefined;
|
|
123
|
+
}) => Effect.Effect<CombinedUserData, DBCallbackFailure | DatabaseError, never>;
|
|
124
|
+
}, never, DBClientLive | StorageManagerResolver>;
|
|
125
|
+
export {};
|