@proteos/sdk 0.54.1 → 0.56.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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The virtual column that stands for a record's rendered title — the entity's
3
+ * Liquid `title_template` evaluated against the row. Lists show it, and the
4
+ * records query sorts and filters on it when the template is "simple". No
5
+ * attribute backs it, so the name is reserved: the backend rejects an
6
+ * attribute called `record_title` (mirror of `metamodel.RecordTitleField`).
7
+ */
8
+ export const RECORD_TITLE_FIELD = 'record_title'
9
+
10
+ /** True when `name` is taken by a virtual column and unavailable to attributes. */
11
+ export function isReservedAttributeName(name: string): boolean {
12
+ return name === RECORD_TITLE_FIELD
13
+ }
14
+
15
+ /**
16
+ * A simple template is literal text plus `{{ attribute }}` references and
17
+ * nothing else — no filters (`| upcase`), no tags (`{% if %}`), no dotted
18
+ * references. Only those compile to SQL, so only those make `record_title`
19
+ * sortable and filterable. Mirror of the Go `titletemplate.ParseSimple`
20
+ * grammar; the backend stays the authority and answers 400 when the two
21
+ * ever disagree.
22
+ */
23
+ export function isTitleQueryable(template: string | undefined): boolean {
24
+ // Nothing to order or match on without at least one attribute reference.
25
+ if (!template?.includes('{{')) return false
26
+ if (template.includes('{%') || template.includes('%}')) return false
27
+ let rest = template
28
+ while (rest !== '') {
29
+ const open = rest.indexOf('{{')
30
+ if (open < 0) return !rest.includes('}}')
31
+ if (rest.slice(0, open).includes('}}')) return false
32
+ rest = rest.slice(open + 2)
33
+ const close = rest.indexOf('}}')
34
+ if (close < 0) return false
35
+ if (!/^[a-z][a-z0-9_]*$/.test(rest.slice(0, close).trim())) return false
36
+ rest = rest.slice(close + 2)
37
+ }
38
+ return true
39
+ }
40
+
41
+ /** The attributes a simple template references, in order of first use; `[]` otherwise. */
42
+ export function titleTemplateVariables(template: string | undefined): string[] {
43
+ if (!template || !isTitleQueryable(template)) return []
44
+ const out: string[] = []
45
+ for (const match of template.matchAll(/\{\{\s*([a-z][a-z0-9_]*)\s*\}\}/g)) {
46
+ const name = match[1]
47
+ if (name && !out.includes(name)) out.push(name)
48
+ }
49
+ return out
50
+ }
@@ -165,7 +165,9 @@ export type PropertyType =
165
165
  | 'credentials_select'
166
166
  | 'button'
167
167
  /** Edits a platform attribute list (`Attribute[]`) via the shared attribute schema builder. */
168
- | 'attribute_schema'
168
+ /** Edits a decision-question map (model-decide `questions`) via the questions builder, with a raw-JSON mode. */
169
+ | 'decision_questions'
170
+ | 'attribute_schema'
169
171
  // Wave 2 (editor support lands with Phase 3):
170
172
  | 'resource_locator'
171
173
  | 'resource_mapper'