@memberjunction/metadata-sync 2.111.1 → 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.
package/README.md CHANGED
@@ -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:
@@ -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;