@memberjunction/metadata-sync 2.112.0 → 2.113.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.
Files changed (42) hide show
  1. package/README.md +136 -61
  2. package/dist/constants/metadata-keywords.d.ts +282 -0
  3. package/dist/constants/metadata-keywords.js +364 -0
  4. package/dist/constants/metadata-keywords.js.map +1 -0
  5. package/dist/lib/EntityPropertyExtractor.d.ts +1 -1
  6. package/dist/lib/EntityPropertyExtractor.js +4 -18
  7. package/dist/lib/EntityPropertyExtractor.js.map +1 -1
  8. package/dist/lib/FieldExternalizer.d.ts +1 -1
  9. package/dist/lib/FieldExternalizer.js +6 -5
  10. package/dist/lib/FieldExternalizer.js.map +1 -1
  11. package/dist/lib/RecordProcessor.d.ts +1 -1
  12. package/dist/lib/RecordProcessor.js +14 -12
  13. package/dist/lib/RecordProcessor.js.map +1 -1
  14. package/dist/lib/RelatedEntityHandler.d.ts +1 -1
  15. package/dist/lib/RelatedEntityHandler.js +5 -5
  16. package/dist/lib/RelatedEntityHandler.js.map +1 -1
  17. package/dist/lib/json-preprocessor.js +7 -6
  18. package/dist/lib/json-preprocessor.js.map +1 -1
  19. package/dist/lib/provider-utils.d.ts +1 -1
  20. package/dist/lib/provider-utils.js +19 -16
  21. package/dist/lib/provider-utils.js.map +1 -1
  22. package/dist/lib/record-dependency-analyzer.js +44 -37
  23. package/dist/lib/record-dependency-analyzer.js.map +1 -1
  24. package/dist/lib/singleton-manager.d.ts +1 -1
  25. package/dist/lib/singleton-manager.js.map +1 -1
  26. package/dist/lib/sync-engine.d.ts +1 -1
  27. package/dist/lib/sync-engine.js +36 -44
  28. package/dist/lib/sync-engine.js.map +1 -1
  29. package/dist/services/PullService.d.ts +1 -1
  30. package/dist/services/PullService.js +18 -22
  31. package/dist/services/PullService.js.map +1 -1
  32. package/dist/services/PushService.d.ts +1 -1
  33. package/dist/services/PushService.js +21 -15
  34. package/dist/services/PushService.js.map +1 -1
  35. package/dist/services/ValidationService.js +32 -44
  36. package/dist/services/ValidationService.js.map +1 -1
  37. package/dist/services/WatchService.d.ts +1 -1
  38. package/dist/services/WatchService.js +14 -13
  39. package/dist/services/WatchService.js.map +1 -1
  40. package/dist/types/validation.d.ts +6 -1
  41. package/dist/types/validation.js.map +1 -1
  42. package/package.json +7 -6
package/README.md CHANGED
@@ -212,7 +212,7 @@ Entity classes are located in:
212
212
 
213
213
  3. **Runtime Metadata Discovery**:
214
214
  ```typescript
215
- import { Metadata } from '@memberjunction/global';
215
+ import { Metadata } from '@memberjunction/core';
216
216
 
217
217
  const md = new Metadata();
218
218
  const entityInfo = md.EntityByName('Templates');
@@ -474,13 +474,13 @@ With `"filePattern": ".*.json"`:
474
474
 
475
475
  ### Troubleshooting Quick Reference
476
476
 
477
- | Error Message | Cause | Solution |
478
- | ---------------------------------------- | ------------------------------------------ | ------------------------------------------------------ |
479
- | `No entity directories found` | Missing .mj-sync.json or wrong filePattern | Check .mj-sync.json exists and uses `"*.json"` |
480
- | `Field 'X' does not exist on entity 'Y'` | Using non-existent field | Check BaseEntity class in entity_subclasses.ts |
481
- | `User ID cannot be null` | Missing required UserID | Add `"UserID": "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E"` |
482
- | `Processing 0 records` | Files don't match filePattern | Check files match pattern in .mj-sync.json |
483
- | Failed validation | Wrong data type or format | Check BaseEntity class for field types |
477
+ | Error Message | Cause | Solution |
478
+ |---------------|-------|----------|
479
+ | `No entity directories found` | Missing .mj-sync.json or wrong filePattern | Check .mj-sync.json exists and uses `"*.json"` |
480
+ | `Field 'X' does not exist on entity 'Y'` | Using non-existent field | Check BaseEntity class in entity_subclasses.ts |
481
+ | `User ID cannot be null` | Missing required UserID | Add `"UserID": "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E"` |
482
+ | `Processing 0 records` | Files don't match filePattern | Check files match pattern in .mj-sync.json |
483
+ | Failed validation | Wrong data type or format | Check BaseEntity class for field types |
484
484
 
485
485
  ### System User ID Reference
486
486
 
@@ -828,13 +828,16 @@ Support environment-specific values:
828
828
  - `@env:VARIABLE_NAME`
829
829
  - Useful for different environments (dev/staging/prod)
830
830
 
831
- ### Automatic JSON Stringification
832
- When a field value is an array or object, the tool automatically converts it to a JSON string for database storage:
833
- - Arrays and objects are detected and stringified with pretty formatting (2-space indentation)
834
- - Maintains clean, readable JSON in source files while storing as strings in database
835
- - Works seamlessly with all field types that accept text content
831
+ ### Automatic JSON Stringification with Reference Processing
836
832
 
837
- Examples:
833
+ When a field value is an array or object, the tool automatically:
834
+ 1. **Recursively processes** all `@lookup:`, `@file:`, `@parent:`, `@root:` references inside the object
835
+ 2. **Converts to JSON string** with pretty formatting (2-space indentation) for database storage
836
+ 3. **Maintains clean structure** in source files while storing as strings in database
837
+
838
+ This is extremely powerful for JSON-typed fields like `Configuration`, `Settings`, `Metadata`, etc.
839
+
840
+ #### Basic Example
838
841
  ```json
839
842
  {
840
843
  "fields": {
@@ -846,16 +849,88 @@ Examples:
846
849
  "items": [1, 2, 3]
847
850
  }
848
851
  },
849
- "Tags": ["tag1", "tag2", "tag3"],
850
- "Metadata": {
851
- "created": "2024-01-15",
852
- "author": "John Doe"
852
+ "Tags": ["tag1", "tag2", "tag3"]
853
+ }
854
+ }
855
+ ```
856
+
857
+ The `Configuration` and `Tags` fields will automatically be converted to JSON strings when pushed to the database.
858
+
859
+ #### Advanced Example: References Inside JSON Fields
860
+
861
+ **This is the powerful part** - you can use `@lookup:` and other references INSIDE object-typed fields:
862
+
863
+ ```json
864
+ {
865
+ "fields": {
866
+ "Name": "Agent Memory Manager Job",
867
+ "CronExpression": "0 */15 * * * *",
868
+ "Configuration": {
869
+ "AgentID": "@lookup:AI Agents.Name=Memory Manager",
870
+ "InitialMessage": "Analyze recent conversations",
871
+ "Settings": {
872
+ "MaxNotes": 5,
873
+ "Strategy": "Relevant",
874
+ "TargetAgentID": "@lookup:AI Agents.Name=Sage"
875
+ }
853
876
  }
854
877
  }
855
878
  }
856
879
  ```
857
880
 
858
- The `Configuration`, `Tags`, and `Metadata` fields will automatically be converted to JSON strings when pushed to the database, while maintaining their structured format in your source files.
881
+ When pushed to the database, this becomes:
882
+ ```json
883
+ {
884
+ "Configuration": "{\"AgentID\":\"actual-uuid-here\",\"InitialMessage\":\"Analyze recent conversations\",\"Settings\":{\"MaxNotes\":5,\"Strategy\":\"Relevant\",\"TargetAgentID\":\"another-uuid-here\"}}"
885
+ }
886
+ ```
887
+
888
+ **Benefits:**
889
+ - ✅ **Human-readable**: Use agent names, not UUIDs in your metadata
890
+ - ✅ **Maintainable**: Changes to entity names don't break references
891
+ - ✅ **Type-safe**: Structured objects in source, properly stringified for DB
892
+ - ✅ **Nested support**: References work at any depth in the object tree
893
+
894
+ #### Common Use Cases
895
+
896
+ **Scheduled Job Configuration:**
897
+ ```json
898
+ {
899
+ "Configuration": {
900
+ "AgentID": "@lookup:AI Agents.Name=Report Generator",
901
+ "Schedule": "daily",
902
+ "Recipients": "@lookup:Users.Email=admin@company.com"
903
+ }
904
+ }
905
+ ```
906
+
907
+ **Action Parameters:**
908
+ ```json
909
+ {
910
+ "DefaultParameters": {
911
+ "TargetEntityID": "@lookup:Entities.Name=Customers",
912
+ "TemplateID": "@lookup:Templates.Name=Welcome Email",
913
+ "Settings": {
914
+ "SendImmediate": true,
915
+ "Priority": "High"
916
+ }
917
+ }
918
+ }
919
+ ```
920
+
921
+ **AI Configuration:**
922
+ ```json
923
+ {
924
+ "AIConfig": {
925
+ "PreferredModelID": "@lookup:AI Models.Name=GPT 4.1",
926
+ "FallbackModels": [
927
+ "@lookup:AI Models.Name=Claude Sonnet 3.7",
928
+ "@lookup:AI Models.Name=Gemini Pro"
929
+ ],
930
+ "Temperature": 0.7
931
+ }
932
+ }
933
+ ```
859
934
 
860
935
  ### {@include} References in Files
861
936
  Enable content composition within non-JSON files (like .md, .html, .txt) using JSDoc-style include syntax:
@@ -1510,13 +1585,13 @@ SQL logging is configured in the root-level `.mj-sync.json` file only (not inher
1510
1585
 
1511
1586
  #### SQL Logging Options
1512
1587
 
1513
- | Option | Type | Default | Description |
1514
- | ------------------- | ---------------------- | --------------- | ------------------------------------------------------------------------------ |
1515
- | `enabled` | boolean | false | Whether to enable SQL logging during push operations |
1516
- | `outputDirectory` | string | "./sql_logging" | Directory to output SQL log files (relative to command execution directory) |
1517
- | `formatAsMigration` | boolean | false | Whether to format SQL as migration-ready files with Flyway schema placeholders |
1518
- | `filterPatterns` | string[] | undefined | Array of patterns to filter SQL statements (see below) |
1519
- | `filterType` | "exclude" \| "include" | "exclude" | How to apply filter patterns |
1588
+ | Option | Type | Default | Description |
1589
+ |--------|------|---------|-------------|
1590
+ | `enabled` | boolean | false | Whether to enable SQL logging during push operations |
1591
+ | `outputDirectory` | string | "./sql_logging" | Directory to output SQL log files (relative to command execution directory) |
1592
+ | `formatAsMigration` | boolean | false | Whether to format SQL as migration-ready files with Flyway schema placeholders |
1593
+ | `filterPatterns` | string[] | undefined | Array of patterns to filter SQL statements (see below) |
1594
+ | `filterType` | "exclude" \| "include" | "exclude" | How to apply filter patterns |
1520
1595
 
1521
1596
  #### SQL Log File Format
1522
1597
 
@@ -1633,11 +1708,11 @@ Add the `userRoleValidation` configuration to your root `.mj-sync.json` file:
1633
1708
 
1634
1709
  #### Configuration Options
1635
1710
 
1636
- | Option | Type | Default | Description |
1637
- | ------------------------ | -------- | ------- | --------------------------------------------- |
1638
- | `enabled` | boolean | false | Enable user role validation for UserID fields |
1639
- | `allowedRoles` | string[] | [] | List of role names that are allowed |
1640
- | `allowUsersWithoutRoles` | boolean | false | Allow users without any assigned roles |
1711
+ | Option | Type | Default | Description |
1712
+ |--------|------|---------|-------------|
1713
+ | `enabled` | boolean | false | Enable user role validation for UserID fields |
1714
+ | `allowedRoles` | string[] | [] | List of role names that are allowed |
1715
+ | `allowUsersWithoutRoles` | boolean | false | Allow users without any assigned roles |
1641
1716
 
1642
1717
  #### How It Works
1643
1718
 
@@ -1864,10 +1939,10 @@ When `recursive: true` is set:
1864
1939
 
1865
1940
  ### Configuration Options
1866
1941
 
1867
- | Option | Type | Default | Description |
1868
- | ----------- | ------- | ------- | ------------------------------------------------- |
1869
- | `recursive` | boolean | false | Enable automatic recursive fetching |
1870
- | `maxDepth` | number | 10 | Maximum recursion depth to prevent infinite loops |
1942
+ | Option | Type | Default | Description |
1943
+ |--------|------|---------|-------------|
1944
+ | `recursive` | boolean | false | Enable automatic recursive fetching |
1945
+ | `maxDepth` | number | 10 | Maximum recursion depth to prevent infinite loops |
1871
1946
 
1872
1947
  ### Safeguards
1873
1948
 
@@ -1934,24 +2009,24 @@ The pull command now supports smart update capabilities with extensive configura
1934
2009
 
1935
2010
  #### Pull Configuration Options
1936
2011
 
1937
- | Option | Type | Default | Description |
1938
- | ----------------------------- | ------------ | ------------------ | ------------------------------------------------------------------------------- |
1939
- | `filePattern` | string | Entity filePattern | Pattern for finding existing files to update |
1940
- | `createNewFileIfNotFound` | boolean | true | Create files for records not found locally |
1941
- | `newFileName` | string | - | Filename for new records when appending (see warning below) |
1942
- | `appendRecordsToExistingFile` | boolean | false | Append new records to a single file |
1943
- | `updateExistingRecords` | boolean | true | Update existing records found in local files |
1944
- | `preserveFields` | string[] | [] | Fields that retain local values during updates (see detailed explanation below) |
1945
- | `mergeStrategy` | string | "merge" | How to merge updates: "merge", "overwrite", or "skip" |
1946
- | `backupBeforeUpdate` | boolean | false | Create timestamped backups before updating files |
1947
- | `backupDirectory` | string | ".backups" | Directory name for backup files (relative to entity directory) |
1948
- | `filter` | string | - | SQL WHERE clause for filtering records |
1949
- | `externalizeFields` | array/object | - | Fields to save as external files with optional patterns |
1950
- | `excludeFields` | string[] | [] | Fields to completely omit from pulled data (see detailed explanation below) |
1951
- | `lookupFields` | object | - | Foreign keys to convert to @lookup references |
1952
- | `relatedEntities` | object | - | Related entities to pull as embedded collections |
1953
- | `ignoreNullFields` | boolean | false | Exclude fields with null values from pulled data |
1954
- | `ignoreVirtualFields` | boolean | false | Exclude virtual fields (view-only fields) from pulled data |
2012
+ | Option | Type | Default | Description |
2013
+ |--------|------|---------|-------------|
2014
+ | `filePattern` | string | Entity filePattern | Pattern for finding existing files to update |
2015
+ | `createNewFileIfNotFound` | boolean | true | Create files for records not found locally |
2016
+ | `newFileName` | string | - | Filename for new records when appending (see warning below) |
2017
+ | `appendRecordsToExistingFile` | boolean | false | Append new records to a single file |
2018
+ | `updateExistingRecords` | boolean | true | Update existing records found in local files |
2019
+ | `preserveFields` | string[] | [] | Fields that retain local values during updates (see detailed explanation below) |
2020
+ | `mergeStrategy` | string | "merge" | How to merge updates: "merge", "overwrite", or "skip" |
2021
+ | `backupBeforeUpdate` | boolean | false | Create timestamped backups before updating files |
2022
+ | `backupDirectory` | string | ".backups" | Directory name for backup files (relative to entity directory) |
2023
+ | `filter` | string | - | SQL WHERE clause for filtering records |
2024
+ | `externalizeFields` | array/object | - | Fields to save as external files with optional patterns |
2025
+ | `excludeFields` | string[] | [] | Fields to completely omit from pulled data (see detailed explanation below) |
2026
+ | `lookupFields` | object | - | Foreign keys to convert to @lookup references |
2027
+ | `relatedEntities` | object | - | Related entities to pull as embedded collections |
2028
+ | `ignoreNullFields` | boolean | false | Exclude fields with null values from pulled data |
2029
+ | `ignoreVirtualFields` | boolean | false | Exclude virtual fields (view-only fields) from pulled data |
1955
2030
 
1956
2031
  > **⚠️ Important Configuration Warning**
1957
2032
  >
@@ -2459,14 +2534,14 @@ When using virtual properties, related required fields are skipped:
2459
2534
 
2460
2535
  ### Common Validation Errors
2461
2536
 
2462
- | Error | Cause | Solution |
2463
- | -------------------------- | ---------------------- | ------------------------------------------ |
2464
- | `Field "X" does not exist` | Typo or wrong entity | Check entity definition in generated files |
2465
- | `Entity "X" not found` | Wrong entity name | Use exact entity name from database |
2466
- | `File not found` | Bad @file: reference | Check file path is relative and exists |
2467
- | `Lookup not found` | No matching record | Verify lookup value or use ?create |
2468
- | `Circular dependency` | A→B→A references | Restructure to avoid cycles |
2469
- | `Required field missing` | Missing required field | Add field with appropriate value |
2537
+ | Error | Cause | Solution |
2538
+ |-------|-------|----------|
2539
+ | `Field "X" does not exist` | Typo or wrong entity | Check entity definition in generated files |
2540
+ | `Entity "X" not found` | Wrong entity name | Use exact entity name from database |
2541
+ | `File not found` | Bad @file: reference | Check file path is relative and exists |
2542
+ | `Lookup not found` | No matching record | Verify lookup value or use ?create |
2543
+ | `Circular dependency` | A→B→A references | Restructure to avoid cycles |
2544
+ | `Required field missing` | Missing required field | Add field with appropriate value |
2470
2545
 
2471
2546
  ### Validation Configuration
2472
2547
 
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Centralized metadata keyword constants for MemberJunction MetadataSync package.
3
+ *
4
+ * These keywords are special prefixes used in metadata JSON files to reference external
5
+ * content, perform lookups, access environment variables, and establish hierarchical relationships.
6
+ *
7
+ * @module metadata-keywords
8
+ *
9
+ * @example
10
+ * // Using @file: to reference external content
11
+ * {
12
+ * "Prompt": "@file:greeting.prompt.md"
13
+ * }
14
+ *
15
+ * @example
16
+ * // Using @lookup: to find an entity by field value
17
+ * {
18
+ * "CategoryID": "@lookup:AI Prompt Categories.Name=Examples"
19
+ * }
20
+ *
21
+ * @example
22
+ * // Using @parent: to reference parent entity fields
23
+ * {
24
+ * "PromptID": "@parent:ID"
25
+ * }
26
+ */
27
+ /**
28
+ * Metadata keyword constants.
29
+ * These are the special @ prefixes recognized by MetadataSync for field value processing.
30
+ */
31
+ export declare const METADATA_KEYWORDS: {
32
+ /**
33
+ * @file: - Loads content from an external file
34
+ *
35
+ * Reads the contents of a file (relative to the JSON metadata file) and uses it as the field value.
36
+ * Supports text files, markdown, JSON, and other formats.
37
+ *
38
+ * @example
39
+ * "@file:greeting.prompt.md"
40
+ * "@file:./shared/common-prompt.md"
41
+ * "@file:../templates/standard-header.md"
42
+ */
43
+ readonly FILE: "@file:";
44
+ /**
45
+ * @lookup: - Looks up an entity record by field value(s)
46
+ *
47
+ * Finds an entity record matching the specified criteria and uses its ID.
48
+ * Supports single-field and multi-field lookups, with optional auto-creation.
49
+ *
50
+ * @example
51
+ * "@lookup:AI Prompt Types.Name=Chat"
52
+ * "@lookup:Users.Email=john@example.com&Department=Sales"
53
+ * "@lookup:Categories.Name=Examples?create"
54
+ * "@lookup:Categories.Name=Examples?create&Description=Example prompts"
55
+ */
56
+ readonly LOOKUP: "@lookup:";
57
+ /**
58
+ * @parent: - References a field from the parent entity
59
+ *
60
+ * In nested/related entity structures, accesses a field value from the immediate parent record.
61
+ * Only valid when processing nested entities that have a parent context.
62
+ *
63
+ * @example
64
+ * "@parent:ID"
65
+ * "@parent:Name"
66
+ * "@parent:CategoryID"
67
+ */
68
+ readonly PARENT: "@parent:";
69
+ /**
70
+ * @root: - References a field from the root entity
71
+ *
72
+ * In nested/related entity structures, accesses a field value from the top-level root record.
73
+ * Only valid when processing nested entities that have a root context.
74
+ *
75
+ * @example
76
+ * "@root:ID"
77
+ * "@root:Name"
78
+ */
79
+ readonly ROOT: "@root:";
80
+ /**
81
+ * @env: - Reads an environment variable
82
+ *
83
+ * Gets the value of an environment variable at runtime.
84
+ * Useful for configuration values that differ between environments.
85
+ *
86
+ * @example
87
+ * "@env:VARIABLE_NAME"
88
+ * "@env:NODE_ENV"
89
+ */
90
+ readonly ENV: "@env:";
91
+ /**
92
+ * @url: - Fetches content from a URL
93
+ *
94
+ * Downloads content from a remote URL and uses it as the field value.
95
+ * Supports HTTP/HTTPS URLs and file:// URLs.
96
+ *
97
+ * @example
98
+ * "@url:https://example.com/prompts/greeting.md"
99
+ * "@url:https://raw.githubusercontent.com/company/prompts/main/customer.md"
100
+ */
101
+ readonly URL: "@url:";
102
+ /**
103
+ * @template: - Loads a template JSON file
104
+ *
105
+ * Loads a JSON template file and uses its contents, replacing any template variables.
106
+ * Useful for standardizing common configurations across multiple records.
107
+ *
108
+ * @example
109
+ * "@template:templates/standard-ai-models.json"
110
+ */
111
+ readonly TEMPLATE: "@template:";
112
+ /**
113
+ * @include or @include.* - Includes content from another file
114
+ *
115
+ * Special directive (not a field value) that merges content from external files.
116
+ * Can be used in both objects and arrays. Supports spread and property modes.
117
+ *
118
+ * @example
119
+ * { "@include": "common-fields.json" }
120
+ * [ "@include:items.json" ]
121
+ * { "@include.models": { "file": "models.json", "mode": "property" } }
122
+ */
123
+ readonly INCLUDE: "@include";
124
+ };
125
+ /**
126
+ * Type representing all metadata keyword values.
127
+ */
128
+ export type MetadataKeyword = typeof METADATA_KEYWORDS[keyof typeof METADATA_KEYWORDS];
129
+ /**
130
+ * Type representing metadata keyword types (without the colon for keywords that have it).
131
+ */
132
+ export type MetadataKeywordType = 'file' | 'lookup' | 'parent' | 'root' | 'env' | 'url' | 'template' | 'include';
133
+ /**
134
+ * Array of all metadata keyword prefixes for iteration.
135
+ * This array maintains the order of keywords for consistent processing.
136
+ */
137
+ export declare const METADATA_KEYWORD_PREFIXES: ReadonlyArray<string>;
138
+ /**
139
+ * Checks if a value is a string that starts with any recognized metadata keyword.
140
+ *
141
+ * This is a type-safe check that handles non-string values gracefully.
142
+ * Returns false for null, undefined, objects, numbers, etc.
143
+ *
144
+ * @param value - The value to check (can be any type)
145
+ * @returns true if value is a string starting with a metadata keyword, false otherwise
146
+ *
147
+ * @example
148
+ * isMetadataKeyword('@file:template.md') // true
149
+ * isMetadataKeyword('@lookup:Users.Email=test@example.com') // true
150
+ * isMetadataKeyword('@parent:ID') // true
151
+ * isMetadataKeyword('regular string') // false
152
+ * isMetadataKeyword(123) // false
153
+ * isMetadataKeyword(null) // false
154
+ * isMetadataKeyword('@unknown:value') // false
155
+ */
156
+ export declare function isMetadataKeyword(value: unknown): value is string;
157
+ /**
158
+ * Determines which metadata keyword type a string value uses.
159
+ *
160
+ * Examines the prefix of a string and returns the corresponding keyword type.
161
+ * Returns null if the value doesn't use any recognized metadata keyword.
162
+ *
163
+ * @param value - The string value to analyze
164
+ * @returns The keyword type ('file', 'lookup', etc.) or null if no keyword is found
165
+ *
166
+ * @example
167
+ * getMetadataKeywordType('@file:template.md') // 'file'
168
+ * getMetadataKeywordType('@lookup:Users.Name=John') // 'lookup'
169
+ * getMetadataKeywordType('@parent:ID') // 'parent'
170
+ * getMetadataKeywordType('@include') // 'include'
171
+ * getMetadataKeywordType('regular string') // null
172
+ * getMetadataKeywordType('@unknown:value') // null
173
+ */
174
+ export declare function getMetadataKeywordType(value: string): MetadataKeywordType | null;
175
+ /**
176
+ * Type-safe check for metadata keywords that handles any value type.
177
+ *
178
+ * This is an alias for isMetadataKeyword() provided for semantic clarity
179
+ * when you want to explicitly check if a value has a metadata keyword.
180
+ *
181
+ * @param value - Any value to check
182
+ * @returns true if value is a string with a metadata keyword, false otherwise
183
+ *
184
+ * @example
185
+ * const fieldValue = record.Get('SomeField');
186
+ * if (hasMetadataKeyword(fieldValue)) {
187
+ * // fieldValue is guaranteed to be a string with a metadata keyword
188
+ * const type = getMetadataKeywordType(fieldValue);
189
+ * }
190
+ */
191
+ export declare function hasMetadataKeyword(value: unknown): value is string;
192
+ /**
193
+ * Checks if a string starts with @ but is NOT a metadata keyword.
194
+ *
195
+ * This is useful for filtering out @ strings that are not metadata keywords,
196
+ * such as npm package names (@mui/material, @angular/core) or email addresses.
197
+ *
198
+ * @param value - The value to check
199
+ * @returns true if value starts with @ but is not a metadata keyword
200
+ *
201
+ * @example
202
+ * isNonKeywordAtSymbol('@mui/material') // true
203
+ * isNonKeywordAtSymbol('@angular/core') // true
204
+ * isNonKeywordAtSymbol('@file:template.md') // false
205
+ * isNonKeywordAtSymbol('regular string') // false
206
+ */
207
+ export declare function isNonKeywordAtSymbol(value: unknown): boolean;
208
+ /**
209
+ * Extracts the value portion after a metadata keyword prefix.
210
+ *
211
+ * Removes the keyword prefix from a string and returns the remaining value.
212
+ * Returns null if the string doesn't use a metadata keyword.
213
+ *
214
+ * @param value - The string containing a metadata keyword
215
+ * @returns The value after the keyword prefix, or null if no keyword found
216
+ *
217
+ * @example
218
+ * extractKeywordValue('@file:template.md') // 'template.md'
219
+ * extractKeywordValue('@lookup:Users.Email=test@example.com') // 'Users.Email=test@example.com'
220
+ * extractKeywordValue('@parent:ID') // 'ID'
221
+ * extractKeywordValue('regular string') // null
222
+ */
223
+ export declare function extractKeywordValue(value: string): string | null;
224
+ /**
225
+ * List of metadata keywords that require a parent context to function.
226
+ * These keywords cannot be used at the top level of metadata - they only work
227
+ * in nested/related entities where a parent record exists.
228
+ */
229
+ export declare const CONTEXT_DEPENDENT_KEYWORDS: readonly ["@parent:", "@root:"];
230
+ /**
231
+ * List of metadata keywords that reference external resources.
232
+ * These keywords load content from files, URLs, or other external sources.
233
+ */
234
+ export declare const EXTERNAL_REFERENCE_KEYWORDS: readonly ["@file:", "@url:", "@template:"];
235
+ /**
236
+ * List of metadata keywords that perform database lookups.
237
+ */
238
+ export declare const LOOKUP_KEYWORDS: readonly ["@lookup:"];
239
+ /**
240
+ * List of metadata keywords that access runtime configuration.
241
+ */
242
+ export declare const RUNTIME_KEYWORDS: readonly ["@env:"];
243
+ /**
244
+ * Checks if a keyword requires a parent/root context.
245
+ *
246
+ * @param keyword - The keyword to check
247
+ * @returns true if the keyword requires context (parent or root)
248
+ *
249
+ * @example
250
+ * isContextDependentKeyword('@parent:') // true
251
+ * isContextDependentKeyword('@root:') // true
252
+ * isContextDependentKeyword('@file:') // false
253
+ */
254
+ export declare function isContextDependentKeyword(keyword: string): boolean;
255
+ /**
256
+ * Checks if a keyword references an external resource.
257
+ *
258
+ * @param keyword - The keyword to check
259
+ * @returns true if the keyword loads external content
260
+ *
261
+ * @example
262
+ * isExternalReferenceKeyword('@file:') // true
263
+ * isExternalReferenceKeyword('@url:') // true
264
+ * isExternalReferenceKeyword('@lookup:') // false
265
+ */
266
+ export declare function isExternalReferenceKeyword(keyword: string): boolean;
267
+ /**
268
+ * Creates a metadata keyword reference string.
269
+ *
270
+ * Helper function to construct properly formatted keyword references.
271
+ * This ensures consistent formatting across the codebase.
272
+ *
273
+ * @param type - The keyword type
274
+ * @param value - The value after the keyword
275
+ * @returns Formatted keyword reference string
276
+ *
277
+ * @example
278
+ * createKeywordReference('file', 'template.md') // '@file:template.md'
279
+ * createKeywordReference('lookup', 'Users.Email=test@example.com') // '@lookup:Users.Email=test@example.com'
280
+ * createKeywordReference('parent', 'ID') // '@parent:ID'
281
+ */
282
+ export declare function createKeywordReference(type: MetadataKeywordType, value: string): string;