@crombolo/core 0.13.1 → 0.14.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crombolo/core",
3
- "version": "0.13.1",
3
+ "version": "0.14.1",
4
4
  "description": "Domain model, entity keys and shared contracts for crombolo. No I/O.",
5
5
  "license": "MIT",
6
6
  "author": "Crombolo",
@@ -36,8 +36,18 @@ export interface PropertyTypeModel {
36
36
  * unchanged on a save, which is what a backoffice save sends.
37
37
  */
38
38
  searchable?: boolean
39
+ /**
40
+ * How much the property counts in site search, or false to leave it out.
41
+ * Absent means unchanged on a save, as for `searchable`.
42
+ */
43
+ siteSearch?: SiteSearchLevel | false
39
44
  }
40
45
 
46
+ /** A property's weight in site search; the page name always ranks above all three. */
47
+ export type SiteSearchLevel = 'high' | 'normal' | 'low'
48
+
49
+ export const SITE_SEARCH_LEVELS: readonly SiteSearchLevel[] = ['high', 'normal', 'low']
50
+
41
51
  export interface PropertyGroupModel {
42
52
  key: string
43
53
  name: string | null
@@ -89,6 +99,8 @@ export interface ContentTypeAggregate {
89
99
  parentKey: string | null
90
100
  sinceVersion?: string | null
91
101
  pending?: { version: string; revision: string } | null
102
+ /** False leaves every page of the type out of site search. Absent means unchanged on a save. */
103
+ siteSearch?: boolean
92
104
  }
93
105
 
94
106
  /**
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from './paging.ts'
10
10
  export * from './permissions.ts'
11
11
  export * from './problem-details.ts'
12
12
  export * from './property-values.ts'
13
+ export * from './public-access.ts'
13
14
  export * from './return-urls.ts'
14
15
  export * from './sections.ts'
15
16
  export * from './uploads.ts'
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Members-only pages: who a protection rule lets in, and which protected
3
+ * branches that leaves a member — one check for rendering a page, the Delivery
4
+ * API and site search. The nearest protected ancestor governs, as in Umbraco.
5
+ */
6
+
7
+ /** Who a protected page lets in: members named by user name, or in a named group. */
8
+ export interface MemberRule {
9
+ memberUserNames: readonly string[]
10
+ memberGroupNames: readonly string[]
11
+ }
12
+
13
+ /** The member asking, by user name and the names of their groups. */
14
+ export interface RuleMember {
15
+ username: string
16
+ groups: readonly string[]
17
+ }
18
+
19
+ /** Whether a rule lets a member in. Names and groups both match in any case. Pure. */
20
+ export function memberMayOpen(rule: MemberRule, member: RuleMember | undefined): boolean {
21
+ if (!member) return false
22
+ const name = member.username.toLowerCase()
23
+ if (rule.memberUserNames.some((n) => n.toLowerCase() === name)) return true
24
+ const groups = new Set(member.groups.map((g) => g.toLowerCase()))
25
+ return rule.memberGroupNames.some((g) => groups.has(g.toLowerCase()))
26
+ }
27
+
28
+ /**
29
+ * The protected nodes a member may not open (`denied`) and may (`allowed`). A
30
+ * page is closed to them when the nearest protected node on its path is
31
+ * denied: a denied branch can hold an allowed one below it. Pure.
32
+ */
33
+ export function branchAccess(
34
+ rules: ReadonlyArray<MemberRule & { nodeId: number }>,
35
+ member: RuleMember | undefined,
36
+ ): { denied: number[]; allowed: number[] } {
37
+ const denied: number[] = []
38
+ const allowed: number[] = []
39
+ for (const rule of rules) (memberMayOpen(rule, member) ? allowed : denied).push(rule.nodeId)
40
+ return { denied, allowed }
41
+ }