@stacksjs/cms 0.74.33 → 0.74.35
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/taggables/fetch.d.ts +14 -0
- package/dist/taggables/fetch.js +1 -1
- package/dist/taggables/index.d.ts +22 -0
- package/dist/taggables/index.js +1 -1
- package/dist/tests/setup.js +13 -1
- package/package.json +8 -8
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import type { TaggableTable } from '@stacksjs/orm';
|
|
2
|
+
/**
|
|
3
|
+
* Which table the `taggable_models` pivot points at (stacksjs/stacks#2579).
|
|
4
|
+
*
|
|
5
|
+
* `tags`, not `taggables`. The four aggregates below joined `taggables`, and
|
|
6
|
+
* every writer of that pivot writes a `tags` id: the dashboard validates
|
|
7
|
+
* `tagIds` against `tags`, writes them in `syncPostRelations`, reads them back
|
|
8
|
+
* in `PostIndexAction`, and counts them in `TagIndexAction`. The migration's own
|
|
9
|
+
* comment says `taggables` and is wrong too.
|
|
10
|
+
*
|
|
11
|
+
* `taggables` is a real table, but a different mechanism - the `taggable` trait
|
|
12
|
+
* in `@stacksjs/orm` writes tag names straight into it with no pivot row at
|
|
13
|
+
* all. Joining the pivot to it produced a silent empty result set, or worse, a
|
|
14
|
+
* row whose id happened to collide.
|
|
15
|
+
*/
|
|
2
16
|
/**
|
|
3
17
|
* Fetch a tag by its ID
|
|
4
18
|
*
|
package/dist/taggables/fetch.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getDb}from"../database";import{findOrCreate}from"./store";export async function fetchTagById(id){const db=await getDb();try{const result=await db.selectFrom("taggables").where("id","=",id).selectAll().executeTakeFirst();if(!result)return;return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tag: ${error.message}`);throw error}}export async function fetchTags(){const db=await getDb();try{return await db.selectFrom("taggables").where("is_active","=",!0).selectAll().execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tags: ${error.message}`);throw error}}export async function firstOrCreate(name,taggableType,description){try{return await findOrCreate({name,taggable_type:taggableType,description})}catch(error){if(error instanceof Error)throw TypeError(`Failed to find or create tag: ${error.message}`);throw error}}export async function countTaggedPosts(taggableType){const db=await getDb();try{const result=await db.selectFrom("taggable_models").where("taggable_type","=",taggableType).count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count tagged posts: ${error.message}`);throw error}}export async function countTotalTags(){const db=await getDb();try{const result=await db.selectFrom("taggables").count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count total tags: ${error.message}`);throw error}}export async function findMostUsedTag(taggableType){const db=await getDb();try{let query=db.selectFrom("taggable_models").innerJoin("
|
|
1
|
+
import{sql}from"@stacksjs/database";import{getDb}from"../database";import{findOrCreate}from"./store";export async function fetchTagById(id){const db=await getDb();try{const result=await db.selectFrom("taggables").where("id","=",id).selectAll().executeTakeFirst();if(!result)return;return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tag: ${error.message}`);throw error}}export async function fetchTags(){const db=await getDb();try{return await db.selectFrom("taggables").where("is_active","=",!0).selectAll().execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tags: ${error.message}`);throw error}}export async function firstOrCreate(name,taggableType,description){try{return await findOrCreate({name,taggable_type:taggableType,description})}catch(error){if(error instanceof Error)throw TypeError(`Failed to find or create tag: ${error.message}`);throw error}}export async function countTaggedPosts(taggableType){const db=await getDb();try{const result=await db.selectFrom("taggable_models").where("taggable_type","=",taggableType).count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count tagged posts: ${error.message}`);throw error}}export async function countTotalTags(){const db=await getDb();try{const result=await db.selectFrom("taggables").count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count total tags: ${error.message}`);throw error}}export async function findMostUsedTag(taggableType){const db=await getDb();try{let query=db.selectFrom("taggable_models").innerJoin("tags","tags.id","=","taggable_models.tag_id").select(["tags.name",sql`count(*)`.as("usage_count")]).groupBy("tags.name");if(taggableType)query=query.where("taggable_models.taggable_type","=",taggableType);const result=await query.orderBy("usage_count","desc").executeTakeFirst();if(!result)return null;return{name:result.name,count:Number(result.usage_count||0)}}catch(error){if(error instanceof Error)throw TypeError(`Failed to find most used tag: ${error.message}`);throw error}}export async function findLeastUsedTag(){const db=await getDb();try{const result=await db.selectFrom("taggable_models").innerJoin("tags","tags.id","=","taggable_models.tag_id").select(["tags.name",sql`count(*)`.as("usage_count")]).groupBy("tags.name").orderBy("usage_count","asc").executeTakeFirst();if(!result)return null;return{name:result.name,count:Number(result.usage_count||0)}}catch(error){if(error instanceof Error)throw TypeError(`Failed to find least used tag: ${error.message}`);throw error}}export async function fetchTagsWithPostCounts(){const db=await getDb();try{return(await db.selectFrom("tags").leftJoin("taggable_models","tags.id","=","taggable_models.tag_id").select(["tags.name",sql`count(taggable_models.id)`.as("post_count")]).groupBy("tags.name").orderBy("post_count","desc").limit(10).execute()).map((row)=>({name:row.name,postCount:Number(row.post_count||0)}))}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tags with post counts: ${error.message}`);throw error}}export async function fetchTagDistribution(){const db=await getDb();try{const typedResult=await db.selectFrom("tags").leftJoin("taggable_models","tags.id","=","taggable_models.tag_id").select(["tags.name",sql`count(taggable_models.id)`.as("count")]).groupBy("tags.name").orderBy("count","desc").execute(),totalCount=typedResult.reduce((sum,row)=>sum+Number(row.count||0),0);return typedResult.map((row)=>({name:row.name,count:Number(row.count||0),percentage:totalCount>0?Number(row.count||0)/totalCount*100:0}))}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tag distribution: ${error.message}`);throw error}}
|
|
@@ -6,6 +6,28 @@ export {
|
|
|
6
6
|
fetchTagById,
|
|
7
7
|
fetchTags,
|
|
8
8
|
} from './fetch';
|
|
9
|
+
/**
|
|
10
|
+
* The tag analytics, which were written but never exported (stacksjs/stacks#2579).
|
|
11
|
+
*
|
|
12
|
+
* Unreachable through `@stacksjs/cms`, which is why two bugs sat in them: they
|
|
13
|
+
* joined the pivot to `taggables` instead of `tags`, and none of them selected
|
|
14
|
+
* the count they then read, so every count was 0 and "most used" was whichever
|
|
15
|
+
* tag sorted first by name. Exported now that they work, since a tag manager is
|
|
16
|
+
* the obvious consumer.
|
|
17
|
+
*/
|
|
18
|
+
export {
|
|
19
|
+
countTaggedPosts,
|
|
20
|
+
countTotalTags,
|
|
21
|
+
fetchTagDistribution,
|
|
22
|
+
fetchTagsWithPostCounts,
|
|
23
|
+
findLeastUsedTag,
|
|
24
|
+
findMostUsedTag,
|
|
25
|
+
firstOrCreate,
|
|
26
|
+
} from './fetch';
|
|
27
|
+
export {
|
|
28
|
+
findOrCreate,
|
|
29
|
+
findOrCreateMany,
|
|
30
|
+
} from './store';
|
|
9
31
|
export {
|
|
10
32
|
store,
|
|
11
33
|
} from './store';
|
package/dist/taggables/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{bulkDestroy,destroy}from"./destroy";export{fetchTagById,fetchTags}from"./fetch";export{store}from"./store";export{update}from"./update";
|
|
1
|
+
export{bulkDestroy,destroy}from"./destroy";export{fetchTagById,fetchTags}from"./fetch";export{countTaggedPosts,countTotalTags,fetchTagDistribution,fetchTagsWithPostCounts,findLeastUsedTag,findMostUsedTag,firstOrCreate}from"./fetch";export{findOrCreate,findOrCreateMany}from"./store";export{store}from"./store";export{update}from"./update";
|
package/dist/tests/setup.js
CHANGED
|
@@ -27,6 +27,18 @@ import{existsSync,unlinkSync}from"node:fs";import{tmpdir}from"node:os";import{jo
|
|
|
27
27
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
28
28
|
updated_at TIMESTAMP
|
|
29
29
|
)
|
|
30
|
+
`).execute();await db.unsafe(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS tags (
|
|
32
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
33
|
+
name VARCHAR(255),
|
|
34
|
+
slug VARCHAR(255),
|
|
35
|
+
description TEXT,
|
|
36
|
+
post_count INTEGER DEFAULT 0,
|
|
37
|
+
color VARCHAR(255),
|
|
38
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
39
|
+
updated_at TIMESTAMP,
|
|
40
|
+
uuid VARCHAR(255)
|
|
41
|
+
)
|
|
30
42
|
`).execute();await db.unsafe(`
|
|
31
43
|
CREATE TABLE IF NOT EXISTS pages (
|
|
32
44
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -97,4 +109,4 @@ import{existsSync,unlinkSync}from"node:fs";import{tmpdir}from"node:os";import{jo
|
|
|
97
109
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
98
110
|
updated_at TIMESTAMP
|
|
99
111
|
)
|
|
100
|
-
`).execute();const tableNames=["categorizables","categorizable_models","taggable_models","pages","page_revisions","redirects","menus","menu_items"];export async function refreshDatabase(){await forceConfig();for(const table of tableNames)await db.unsafe(`DELETE FROM ${table}`).execute()}process.on("exit",()=>{for(const suffix of["","-wal","-shm"])try{if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`)}catch{}});
|
|
112
|
+
`).execute();const tableNames=["categorizables","categorizable_models","taggable_models","tags","pages","page_revisions","redirects","menus","menu_items"];export async function refreshDatabase(){await forceConfig();for(const table of tableNames)await db.unsafe(`DELETE FROM ${table}`).execute()}process.on("exit",()=>{for(const suffix of["","-wal","-shm"])try{if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`)}catch{}});
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/cms",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.35",
|
|
6
6
|
"description": "Stacks cms utilities.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"prepublishOnly": "bun run build"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@stacksjs/database": "0.74.
|
|
62
|
-
"@stacksjs/error-handling": "0.74.
|
|
63
|
-
"@stacksjs/orm": "0.74.
|
|
64
|
-
"@stacksjs/path": "0.74.
|
|
65
|
-
"@stacksjs/sites": "0.74.
|
|
66
|
-
"@stacksjs/slug": "0.74.
|
|
61
|
+
"@stacksjs/database": "0.74.35",
|
|
62
|
+
"@stacksjs/error-handling": "0.74.35",
|
|
63
|
+
"@stacksjs/orm": "0.74.35",
|
|
64
|
+
"@stacksjs/path": "0.74.35",
|
|
65
|
+
"@stacksjs/sites": "0.74.35",
|
|
66
|
+
"@stacksjs/slug": "0.74.35",
|
|
67
67
|
"@stacksjs/stx": "^0.2.274",
|
|
68
|
-
"@stacksjs/validation": "0.74.
|
|
68
|
+
"@stacksjs/validation": "0.74.35",
|
|
69
69
|
"ts-slug": "^0.1.0",
|
|
70
70
|
"ts-spreadsheets": "^0.2.0"
|
|
71
71
|
},
|