@kernhq/module-quire 0.16.2 → 0.16.4
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/package.json +2 -1
- package/src/client/i18n.ts +31 -0
- package/src/contract/permissions.test.ts +49 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernhq/module-quire",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
4
4
|
"description": "Kern Quire: collaborative documents, spaces and page trees",
|
|
5
5
|
"homepage": "https://github.com/KernAIO/module-quire#readme",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"@changesets/cli": "^2.27.0",
|
|
72
72
|
"@kernhq/contracts": "^0.7.0",
|
|
73
73
|
"@kernhq/kernel": "^0.9.0",
|
|
74
|
+
"@kernhq/testing": "^0.1.12",
|
|
74
75
|
"@kernhq/tsconfig": "^0.1.0",
|
|
75
76
|
"@kernhq/ui": "^0.14.0",
|
|
76
77
|
"@tanstack/svelte-query": "^6.1.0",
|
package/src/client/i18n.ts
CHANGED
|
@@ -90,6 +90,12 @@ export const en: Record<string, Message> = {
|
|
|
90
90
|
'quire.unarchive': 'Take out of the archive',
|
|
91
91
|
'quire.unpublished': 'This draft has changes readers cannot see yet',
|
|
92
92
|
'quire.untitled': 'Untitled',
|
|
93
|
+
'quire.macro_pick_page': 'Pick a page',
|
|
94
|
+
'quire.macro_pick_page_desc': 'The page this block will show. Search by title, or browse a space.',
|
|
95
|
+
'quire.macro_pick_search': 'Search pages',
|
|
96
|
+
'quire.macro_pick_space': 'Space',
|
|
97
|
+
'quire.macro_pick_none': 'No page matches',
|
|
98
|
+
'quire.macro_pick_none_desc': 'Try another word, or pick a different space.',
|
|
93
99
|
'quire.version_auto': 'While writing',
|
|
94
100
|
'quire.version_imported': 'Imported',
|
|
95
101
|
'quire.version_live': 'What readers see',
|
|
@@ -802,6 +808,12 @@ const ar: Record<string, Message> = {
|
|
|
802
808
|
'quire.unarchive': 'إخراج من الأرشيف',
|
|
803
809
|
'quire.unpublished': 'تحتوي هذه المسودة على تغييرات لا يراها القرّاء بعد',
|
|
804
810
|
'quire.untitled': 'بلا عنوان',
|
|
811
|
+
'quire.macro_pick_page': 'اختر صفحة',
|
|
812
|
+
'quire.macro_pick_page_desc': 'الصفحة التي سيعرضها هذا العنصر. ابحث بالعنوان أو تصفّح مساحة.',
|
|
813
|
+
'quire.macro_pick_search': 'البحث في الصفحات',
|
|
814
|
+
'quire.macro_pick_space': 'المساحة',
|
|
815
|
+
'quire.macro_pick_none': 'لا توجد صفحة مطابقة',
|
|
816
|
+
'quire.macro_pick_none_desc': 'جرّب كلمة أخرى، أو اختر مساحة مختلفة.',
|
|
805
817
|
'quire.version_auto': 'أثناء الكتابة',
|
|
806
818
|
'quire.version_imported': 'مستوردة',
|
|
807
819
|
'quire.version_live': 'ما يراه القرّاء',
|
|
@@ -1519,6 +1531,13 @@ const de: Record<string, Message> = {
|
|
|
1519
1531
|
'quire.unarchive': 'Aus dem Archiv holen',
|
|
1520
1532
|
'quire.unpublished': 'Dieser Entwurf hat Änderungen, die Lesende noch nicht sehen',
|
|
1521
1533
|
'quire.untitled': 'Ohne Titel',
|
|
1534
|
+
'quire.macro_pick_page': 'Seite auswählen',
|
|
1535
|
+
'quire.macro_pick_page_desc':
|
|
1536
|
+
'Die Seite, die dieser Block zeigt. Nach Titel suchen oder einen Bereich durchsehen.',
|
|
1537
|
+
'quire.macro_pick_search': 'Seiten durchsuchen',
|
|
1538
|
+
'quire.macro_pick_space': 'Bereich',
|
|
1539
|
+
'quire.macro_pick_none': 'Keine passende Seite',
|
|
1540
|
+
'quire.macro_pick_none_desc': 'Ein anderes Wort versuchen oder einen anderen Bereich wählen.',
|
|
1522
1541
|
'quire.version_auto': 'Beim Schreiben',
|
|
1523
1542
|
'quire.version_imported': 'Importiert',
|
|
1524
1543
|
'quire.version_live': 'Was Lesende sehen',
|
|
@@ -2213,6 +2232,12 @@ const fa: Record<string, Message> = {
|
|
|
2213
2232
|
'quire.unarchive': 'خارج کردن از بایگانی',
|
|
2214
2233
|
'quire.unpublished': 'این پیشنویس تغییرهایی دارد که خوانندگان هنوز نمیبینند',
|
|
2215
2234
|
'quire.untitled': 'بیعنوان',
|
|
2235
|
+
'quire.macro_pick_page': 'انتخاب صفحه',
|
|
2236
|
+
'quire.macro_pick_page_desc': 'صفحهای که این بلوک نشان میدهد. با عنوان جستوجو کنید یا در یک فضا بگردید.',
|
|
2237
|
+
'quire.macro_pick_search': 'جستوجوی صفحهها',
|
|
2238
|
+
'quire.macro_pick_space': 'فضا',
|
|
2239
|
+
'quire.macro_pick_none': 'صفحهای پیدا نشد',
|
|
2240
|
+
'quire.macro_pick_none_desc': 'واژهٔ دیگری را امتحان کنید یا فضای دیگری را برگزینید.',
|
|
2216
2241
|
'quire.version_auto': 'هنگام نوشتن',
|
|
2217
2242
|
'quire.version_imported': 'وارد شد',
|
|
2218
2243
|
'quire.version_live': 'آنچه خوانندگان میبینند',
|
|
@@ -2880,6 +2905,12 @@ const tr: Record<string, Message> = {
|
|
|
2880
2905
|
'quire.unarchive': 'Arşivden çıkar',
|
|
2881
2906
|
'quire.unpublished': 'Bu taslakta okuyucuların henüz göremediği değişiklikler var',
|
|
2882
2907
|
'quire.untitled': 'Adsız',
|
|
2908
|
+
'quire.macro_pick_page': 'Sayfa seç',
|
|
2909
|
+
'quire.macro_pick_page_desc': 'Bu bloğun göstereceği sayfa. Başlığa göre arayın ya da bir alana göz atın.',
|
|
2910
|
+
'quire.macro_pick_search': 'Sayfalarda ara',
|
|
2911
|
+
'quire.macro_pick_space': 'Alan',
|
|
2912
|
+
'quire.macro_pick_none': 'Eşleşen sayfa yok',
|
|
2913
|
+
'quire.macro_pick_none_desc': 'Başka bir sözcük deneyin ya da farklı bir alan seçin.',
|
|
2883
2914
|
'quire.version_auto': 'Yazarken',
|
|
2884
2915
|
'quire.version_imported': 'İçe aktarıldı',
|
|
2885
2916
|
'quire.version_live': 'Okuyucuların gördüğü',
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Quire permission matrix, blessed rather than assumed.
|
|
3
|
+
*
|
|
4
|
+
* Defaults are declared one permission at a time, which makes the whole picture — which built-in
|
|
5
|
+
* role ends up holding what — impossible to read from any single line. This writes it out in full
|
|
6
|
+
* and compares it against what the module declares. Rows list the *effective* grants, cascade
|
|
7
|
+
* included: the kernel expands declared `defaultRoles` upward through guest ⊆ member ⊆ admin ⊆
|
|
8
|
+
* owner, and `permissionMatrixDiff` applies the same expansion.
|
|
9
|
+
*
|
|
10
|
+
* Changing a default is meant to be deliberate: edit `defaultRoles` → this fails naming every row
|
|
11
|
+
* that moved → confirm that is what you meant → update `BLESSED` in the same commit.
|
|
12
|
+
*/
|
|
13
|
+
import { permissionMatrixDiff } from '@kernhq/testing'
|
|
14
|
+
import { describe, expect, it } from 'vitest'
|
|
15
|
+
import { quirePermissions } from './permissions.js'
|
|
16
|
+
|
|
17
|
+
/** Every built-in role that holds the permission by default, lowest role first. */
|
|
18
|
+
const BLESSED: Record<string, readonly string[]> = {
|
|
19
|
+
'quire.space.view': ['guest', 'member', 'admin', 'owner'],
|
|
20
|
+
'quire.space.manage': ['admin', 'owner'],
|
|
21
|
+
'quire.page.view': ['guest', 'member', 'admin', 'owner'],
|
|
22
|
+
'quire.page.create': ['member', 'admin', 'owner'],
|
|
23
|
+
'quire.page.edit': ['member', 'admin', 'owner'],
|
|
24
|
+
'quire.page.comment': ['guest', 'member', 'admin', 'owner'],
|
|
25
|
+
'quire.page.publish': ['member', 'admin', 'owner'],
|
|
26
|
+
'quire.page.export': ['member', 'admin', 'owner'],
|
|
27
|
+
'quire.page.import': ['admin', 'owner'],
|
|
28
|
+
'quire.page.delete': ['admin', 'owner'],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Permissions whose misuse costs data: an import overwrites, a delete removes. */
|
|
32
|
+
const DANGEROUS = ['quire.page.import', 'quire.page.delete']
|
|
33
|
+
|
|
34
|
+
describe('quire permissions', () => {
|
|
35
|
+
it('grants each permission to exactly the blessed roles', () => {
|
|
36
|
+
expect(permissionMatrixDiff(quirePermissions, BLESSED)).toEqual([])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('namespaces every key under the module id and declares it once', () => {
|
|
40
|
+
const keys = quirePermissions.map((p) => p.key)
|
|
41
|
+
expect(keys.filter((key) => !key.startsWith('quire.'))).toEqual([])
|
|
42
|
+
expect(keys.filter((key, i) => keys.indexOf(key) !== i)).toEqual([])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('marks exactly the destructive permissions dangerous', () => {
|
|
46
|
+
const flagged = quirePermissions.filter((p) => p.dangerous).map((p) => p.key)
|
|
47
|
+
expect(flagged.toSorted()).toEqual(DANGEROUS.toSorted())
|
|
48
|
+
})
|
|
49
|
+
})
|