@encodeagent/platform-helper-util 1.2603.1221241 → 1.2603.1311450

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 (69) hide show
  1. package/README.md +277 -153
  2. package/dist/api.d.ts +38 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +38 -2
  5. package/dist/api.js.map +1 -1
  6. package/dist/auth.d.ts +93 -0
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js +93 -2
  9. package/dist/auth.js.map +1 -1
  10. package/dist/colors.d.ts +41 -0
  11. package/dist/colors.d.ts.map +1 -1
  12. package/dist/colors.js +41 -0
  13. package/dist/colors.js.map +1 -1
  14. package/dist/core.d.ts +262 -0
  15. package/dist/core.d.ts.map +1 -1
  16. package/dist/core.js +264 -2
  17. package/dist/core.js.map +1 -1
  18. package/dist/cost.d.ts +26 -0
  19. package/dist/cost.d.ts.map +1 -1
  20. package/dist/cost.js +27 -1
  21. package/dist/cost.js.map +1 -1
  22. package/dist/file.d.ts +38 -0
  23. package/dist/file.d.ts.map +1 -1
  24. package/dist/file.js +42 -2
  25. package/dist/file.js.map +1 -1
  26. package/dist/html.d.ts +83 -0
  27. package/dist/html.d.ts.map +1 -1
  28. package/dist/html.js +88 -35
  29. package/dist/html.js.map +1 -1
  30. package/dist/logger.d.ts +17 -0
  31. package/dist/logger.d.ts.map +1 -1
  32. package/dist/logger.js +28 -2
  33. package/dist/logger.js.map +1 -1
  34. package/dist/markdown.d.ts +20 -0
  35. package/dist/markdown.d.ts.map +1 -1
  36. package/dist/markdown.js +20 -0
  37. package/dist/markdown.js.map +1 -1
  38. package/dist/metadata.js +1 -1
  39. package/dist/metadata.js.map +1 -1
  40. package/dist/record.d.ts +137 -5
  41. package/dist/record.d.ts.map +1 -1
  42. package/dist/record.js +184 -19
  43. package/dist/record.js.map +1 -1
  44. package/dist/shortid.d.ts +21 -8
  45. package/dist/shortid.d.ts.map +1 -1
  46. package/dist/shortid.js +9 -5
  47. package/dist/shortid.js.map +1 -1
  48. package/dist/slug.d.ts +10 -0
  49. package/dist/slug.d.ts.map +1 -1
  50. package/dist/slug.js +10 -0
  51. package/dist/slug.js.map +1 -1
  52. package/dist/token.d.ts +1 -1
  53. package/dist/tree.d.ts +43 -0
  54. package/dist/tree.d.ts.map +1 -1
  55. package/dist/tree.js +43 -5
  56. package/dist/tree.js.map +1 -1
  57. package/dist/value-of-object.d.ts +41 -0
  58. package/dist/value-of-object.d.ts.map +1 -1
  59. package/dist/value-of-object.js +59 -17
  60. package/dist/value-of-object.js.map +1 -1
  61. package/dist/web-content.d.ts +52 -0
  62. package/dist/web-content.d.ts.map +1 -1
  63. package/dist/web-content.js +52 -0
  64. package/dist/web-content.js.map +1 -1
  65. package/dist/web.d.ts +51 -0
  66. package/dist/web.d.ts.map +1 -1
  67. package/dist/web.js +54 -1
  68. package/dist/web.js.map +1 -1
  69. package/package.json +20 -15
package/dist/record.d.ts CHANGED
@@ -23,31 +23,72 @@ export interface IRecord {
23
23
  organizationId: string;
24
24
  solutionId: string;
25
25
  createdBy: string;
26
- createdOn: number;
26
+ createdAt: number;
27
27
  ownedBy: string;
28
- ownedOn: number;
28
+ ownedAt: number;
29
29
  modifiedBy: string;
30
- modifiedOn: number;
30
+ modifiedAt: number;
31
31
  partitionKey: string;
32
32
  isPublished?: boolean;
33
33
  isGlobal?: boolean;
34
34
  isPublishedBy?: string;
35
- isPublishedOn?: string;
35
+ isPublishedAt?: string;
36
36
  isGlobalBy?: string;
37
- isGlobalOn?: string;
37
+ isGlobalAt?: string;
38
38
  stateCode: number;
39
39
  statusCode: number;
40
40
  [key: string]: any;
41
41
  }
42
+ /**
43
+ * Strips server-managed and identity fields from a record so it can be submitted as a
44
+ * new create payload. Removes audit timestamps, CosmosDB system properties (`_rid`,
45
+ * `_self`, etc.), computed fields (`slug`, `searchDisplay`, `display`), and platform
46
+ * fields (`stateCode`, `statusCode`, `organizationId`, `solutionId`, `partitionKey`).
47
+ * Assigns a fresh GUID as `id`.
48
+ */
42
49
  export declare const convertRecordForCreate: (record: Record<string, any>) => Record<string, any>;
43
50
  export interface IGetBaseRecordProps extends IContext {
44
51
  entityName: string;
45
52
  entityType?: string;
46
53
  }
54
+ /**
55
+ * Creates a minimal valid `IRecord` shell with a new GUID and audit fields populated
56
+ * from the provided context. All timestamps are set to the current moment.
57
+ * `partitionKey` is set to `organizationId` (CosmosDB convention).
58
+ */
47
59
  export declare const getBaseRecord: (context: IGetBaseRecordProps) => IRecord;
60
+ /**
61
+ * Returns the first non-empty string value found by iterating `displayFields` in order.
62
+ * Used internally by `getRecordDisplay` when an entity defines explicit display field names.
63
+ */
48
64
  export declare const getRecordDisplayByDisplayFields: (record: Record<string, any>, displayFields?: string[]) => string;
65
+ /**
66
+ * Returns a folder path string derived from a record's `entityName` and `entityType`.
67
+ * Format: `"entityName/entityType"` or just `"entityName"` when `entityNameOnly` is true
68
+ * or when `entityType` is absent. Returns `"Unknown"` when both are empty.
69
+ */
49
70
  export declare const getRecordEntityFolder: (data: Record<string, any>, entityNameOnly: boolean) => string;
71
+ /**
72
+ * Builds a storage folder path from explicit `entityName` and optional `entityType` strings
73
+ * (rather than reading from a record object). Returns `"Unknown"` when `entityName` is empty.
74
+ */
50
75
  export declare const getEntityFolder: (entityName: string, entityType?: string) => string;
76
+ /**
77
+ * Resolves the best human-readable display label for a record.
78
+ *
79
+ * Resolution priority:
80
+ * 1. Search highlight title (`_ui.highlight.title`)
81
+ * 2. `searchDisplay` (when `includeSearchDisplay` is true)
82
+ * 3. Entity `displayFields` (first non-empty field)
83
+ * 4. `firstName + lastName` full name
84
+ * 5. `displayValue`
85
+ * 6. `number – title` / `number – name` / `number – subject`
86
+ * 7. `display`, `text`, `symbol`, `code`
87
+ *
88
+ * The result is cleaned (normalizes whitespace and punctuation spacing) and optionally
89
+ * truncated to `maxLength` characters. Returns the record `id` when `allowUseRecordId`
90
+ * is true and no other value could be resolved.
91
+ */
51
92
  export declare const getRecordDisplay: (record: Record<string, any>, settings?: {
52
93
  entity?: Record<string, any>;
53
94
  displayFields?: string[];
@@ -56,13 +97,38 @@ export declare const getRecordDisplay: (record: Record<string, any>, settings?:
56
97
  emptyReturnUndefined?: boolean;
57
98
  allowUseRecordId?: boolean;
58
99
  }) => string | undefined;
100
+ /**
101
+ * Resolves the best short abstract/description text for a record.
102
+ *
103
+ * Resolution priority:
104
+ * 1. Search highlight (`highlight.searchContent[0]`)
105
+ * 2. `summary`, `description`, `abstract`
106
+ * 3. `searchContent` (only when `settings.includeContent` is true)
107
+ *
108
+ * Whitespace and punctuation are normalized before returning.
109
+ * Truncates to `settings.maxLength` characters when provided.
110
+ */
59
111
  export declare const getRecordAbstract: (record: Record<string, any>, settings?: {
60
112
  entity?: Record<string, any>;
61
113
  abstractFields?: string[];
62
114
  maxLength?: number;
63
115
  includeContent?: boolean;
64
116
  }) => string;
117
+ /**
118
+ * Returns the first non-empty string value found by iterating `contentFields` in order.
119
+ * Used internally by `getRecordContent` when an entity defines explicit content field names.
120
+ */
65
121
  export declare const getRecordContentByContentFields: (record: Record<string, any>, contentFields?: string[]) => string;
122
+ /**
123
+ * Resolves the main body content for a record, suitable for indexing or display.
124
+ *
125
+ * Resolution priority:
126
+ * 1. Search highlight (`_ui.highlight.content`)
127
+ * 2. `searchContent` (when `includeSearchDisplay` is true)
128
+ * 3. Entity/settings `contentFields` (defaults to description, note, detail, content, …)
129
+ *
130
+ * Truncates to `maxLength` when provided.
131
+ */
66
132
  export declare const getRecordContent: (record: Record<string, any>, settings?: {
67
133
  entity?: Record<string, any>;
68
134
  contentFields?: string[];
@@ -70,18 +136,60 @@ export declare const getRecordContent: (record: Record<string, any>, settings?:
70
136
  maxLength?: number;
71
137
  emptyReturnUndefined?: boolean;
72
138
  }) => string | undefined;
139
+ /**
140
+ * Formats a complete postal address from prefixed address fields on a record
141
+ * (e.g. `prefix = "billing"` reads `billingLine1`, `billingCity`, `billingState`, …).
142
+ * Returns `""` when the record does not contain the minimum required fields
143
+ * (city + state + country, or postalCode + country).
144
+ */
73
145
  export declare const getRecordAddress: (record: Record<string, any>, prefix: string) => string;
146
+ /**
147
+ * Returns the primary media URL for a record, trying `media`, `photoUrl`, and
148
+ * `source.photoUrl` in that order. Returns `""` when none are set.
149
+ */
74
150
  export declare const getRecordMedia: (record: Record<string, any>) => string;
151
+ /**
152
+ * Constructs a display name for a user-like record.
153
+ *
154
+ * Resolution order: `fullName` → `firstName + lastName` → `name` → `email`.
155
+ * `skipFullNameProp`, `skipNameProp`, `skipEmailProp` let callers exclude specific steps.
156
+ * Simplified Chinese locale (Windows locale code 2052) has spaces stripped from the assembled name.
157
+ * Multiple consecutive spaces are collapsed to a single space.
158
+ */
75
159
  export declare const getRecordFullName: (user: Record<string, any>, settings?: {
76
160
  skipFullNameProp?: boolean;
77
161
  skipNameProp?: boolean;
78
162
  skipEmailProp?: boolean;
79
163
  }) => string;
164
+ /**
165
+ * Returns a formatted email address string for a record that has a valid `email` field.
166
+ * Format: `"Full Name <email@example.com>"`, or just `"email@example.com"` when
167
+ * `emailOnly` is true or no display name is available.
168
+ * Returns `undefined` when the record has no valid email.
169
+ */
80
170
  export declare const getRecordEmailAddress: (record: Record<string, any>, emailOnly?: boolean) => string | undefined;
171
+ /**
172
+ * Builds an Open Graph / page metadata object for a record, suitable for `<head>` tags.
173
+ * Returns `{ id, title, description, type: "article", image? }`.
174
+ * Title is derived from `getRecordDisplay` (or `getRecordAbstract` when display is empty),
175
+ * optionally prefixed with `settings.siteName`.
176
+ */
81
177
  export declare const getRecordPageMetadata: (record: Record<string, any>, settings?: {
82
178
  siteName?: string;
83
179
  }) => Record<string, any>;
180
+ /**
181
+ * Computes the current slug via `getRecordSlug` and ensures it is present in both
182
+ * `record.slug` (current) and `record.slugs` (historical list, deduplicated).
183
+ * Call this before persisting a record to keep slug history intact for redirects.
184
+ */
84
185
  export declare const applyRecordSlug: (record: Record<string, any>) => Record<string, any>;
186
+ /**
187
+ * Derives a URL slug for a record.
188
+ * Uses `record.customSlug` when set; otherwise derives from `getRecordDisplay`.
189
+ * The slug is truncated to 128 characters and suffixed with the second GUID segment
190
+ * of the record ID (e.g. `"my-post-e29b"`) to guarantee global uniqueness.
191
+ * Returns `undefined` when the record has no `id`.
192
+ */
85
193
  export declare const getRecordSlug: (record: Record<string, any>) => string | undefined;
86
194
  export type TRecordFilePath = {
87
195
  solutionId: string;
@@ -90,9 +198,33 @@ export type TRecordFilePath = {
90
198
  entityName: string;
91
199
  fieldName?: string;
92
200
  };
201
+ /**
202
+ * Builds the storage path for a file attached to a record.
203
+ * Format: `"{solutionId}/{organizationId}/{entityName}/{recordId}/{fieldName}"`.
204
+ * `fieldName` defaults to `"file"` when not provided.
205
+ */
93
206
  export declare const getRecordFilePath: (props: TRecordFilePath) => string;
207
+ /**
208
+ * Extracts an `IContext` from a record that carries context fields directly
209
+ * (e.g. an API request body that has been enriched with caller context).
210
+ * Picks `solutionId`, `domain`, `sourceIp`, `userId`, `organizationId`,
211
+ * `user`, `organization`, and `solution`.
212
+ */
94
213
  export declare const getContextFromRecord: (record: Record<string, any>) => IContext;
214
+ /**
215
+ * Returns true when a record is marked global but belongs to a different organization
216
+ * than the one in the current context. Used to identify marketplace/shared content.
217
+ */
95
218
  export declare const fromOtherOrganization: (context: IContext, record: Record<string, any>) => boolean;
219
+ /**
220
+ * Returns true when the record's ID appears in the current user's favorites list
221
+ * for the record's entity type (`context.user.favorites[entityName]`).
222
+ */
96
223
  export declare const isFavorite: (context: IContext, record: Record<string, any>) => boolean;
224
+ /**
225
+ * Returns true when a record is global and its owning organization differs from the
226
+ * current context's organization. Equivalent to `fromOtherOrganization` but compares
227
+ * against `context.organization.id` instead of `context.organizationId`.
228
+ */
97
229
  export declare const isFromMarketplace: (context: Record<string, any>, record: Record<string, any>) => boolean;
98
230
  //# sourceMappingURL=record.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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"}
package/dist/record.js CHANGED
@@ -21,14 +21,21 @@ exports.isFromMarketplace = exports.isFavorite = exports.fromOtherOrganization =
21
21
  const lodash_1 = require("lodash");
22
22
  const core_1 = require("./core");
23
23
  const slug_1 = require("./slug");
24
+ /**
25
+ * Strips server-managed and identity fields from a record so it can be submitted as a
26
+ * new create payload. Removes audit timestamps, CosmosDB system properties (`_rid`,
27
+ * `_self`, etc.), computed fields (`slug`, `searchDisplay`, `display`), and platform
28
+ * fields (`stateCode`, `statusCode`, `organizationId`, `solutionId`, `partitionKey`).
29
+ * Assigns a fresh GUID as `id`.
30
+ */
24
31
  const convertRecordForCreate = (record) => {
25
32
  const newRecord = (0, lodash_1.cloneDeep)(record);
26
33
  delete newRecord.createdBy;
27
- delete newRecord.createdOn;
34
+ delete newRecord.createdAt;
28
35
  delete newRecord.ownedBy;
29
- delete newRecord.ownedOn;
36
+ delete newRecord.ownedAt;
30
37
  delete newRecord.modifiedBy;
31
- delete newRecord.modifiedOn;
38
+ delete newRecord.modifiedAt;
32
39
  delete newRecord["_rid"];
33
40
  delete newRecord["_self"];
34
41
  delete newRecord["_etag"];
@@ -52,6 +59,11 @@ const convertRecordForCreate = (record) => {
52
59
  return newRecord;
53
60
  };
54
61
  exports.convertRecordForCreate = convertRecordForCreate;
62
+ /**
63
+ * Creates a minimal valid `IRecord` shell with a new GUID and audit fields populated
64
+ * from the provided context. All timestamps are set to the current moment.
65
+ * `partitionKey` is set to `organizationId` (CosmosDB convention).
66
+ */
55
67
  const getBaseRecord = (context) => {
56
68
  const { userId, organizationId, solutionId, entityName, entityType } = context;
57
69
  return {
@@ -59,19 +71,23 @@ const getBaseRecord = (context) => {
59
71
  entityName,
60
72
  entityType,
61
73
  createdBy: userId,
62
- createdOn: (0, core_1.getTimestamp)(),
74
+ createdAt: (0, core_1.getTimestamp)(),
63
75
  ownedBy: userId,
64
- ownedOn: (0, core_1.getTimestamp)(),
76
+ ownedAt: (0, core_1.getTimestamp)(),
65
77
  modifiedBy: userId,
66
- modifiedOn: (0, core_1.getTimestamp)(),
78
+ modifiedAt: (0, core_1.getTimestamp)(),
67
79
  stateCode: 0,
68
80
  statusCode: 0,
69
81
  organizationId,
70
82
  solutionId,
71
- partitionKey: organizationId,
83
+ partitionKey: organizationId
72
84
  };
73
85
  };
74
86
  exports.getBaseRecord = getBaseRecord;
87
+ /**
88
+ * Returns the first non-empty string value found by iterating `displayFields` in order.
89
+ * Used internally by `getRecordDisplay` when an entity defines explicit display field names.
90
+ */
75
91
  const getRecordDisplayByDisplayFields = (record, displayFields) => {
76
92
  let result = "";
77
93
  if (displayFields && (0, lodash_1.isArray)(displayFields) && !(0, core_1.isNonEmptyString)(result)) {
@@ -82,13 +98,24 @@ const getRecordDisplayByDisplayFields = (record, displayFields) => {
82
98
  return result;
83
99
  };
84
100
  exports.getRecordDisplayByDisplayFields = getRecordDisplayByDisplayFields;
101
+ /**
102
+ * Returns a folder path string derived from a record's `entityName` and `entityType`.
103
+ * Format: `"entityName/entityType"` or just `"entityName"` when `entityNameOnly` is true
104
+ * or when `entityType` is absent. Returns `"Unknown"` when both are empty.
105
+ */
85
106
  const getRecordEntityFolder = (data, entityNameOnly) => {
86
- let entityFolder = (0, core_1.isNonEmptyString)(data.entityType) && !entityNameOnly ? `${data.entityName ?? ""}/${data.entityType}` : (data.entityName ?? "");
107
+ let entityFolder = (0, core_1.isNonEmptyString)(data.entityType) && !entityNameOnly
108
+ ? `${data.entityName ?? ""}/${data.entityType}`
109
+ : (data.entityName ?? "");
87
110
  if (entityFolder.length == 0)
88
111
  entityFolder = "Unknown";
89
112
  return entityFolder;
90
113
  };
91
114
  exports.getRecordEntityFolder = getRecordEntityFolder;
115
+ /**
116
+ * Builds a storage folder path from explicit `entityName` and optional `entityType` strings
117
+ * (rather than reading from a record object). Returns `"Unknown"` when `entityName` is empty.
118
+ */
92
119
  const getEntityFolder = (entityName, entityType) => {
93
120
  let entityFolder = (0, core_1.isNonEmptyString)(entityType) ? `${entityName}/${entityType}` : entityName;
94
121
  if (entityFolder.length == 0)
@@ -96,6 +123,22 @@ const getEntityFolder = (entityName, entityType) => {
96
123
  return entityFolder;
97
124
  };
98
125
  exports.getEntityFolder = getEntityFolder;
126
+ /**
127
+ * Resolves the best human-readable display label for a record.
128
+ *
129
+ * Resolution priority:
130
+ * 1. Search highlight title (`_ui.highlight.title`)
131
+ * 2. `searchDisplay` (when `includeSearchDisplay` is true)
132
+ * 3. Entity `displayFields` (first non-empty field)
133
+ * 4. `firstName + lastName` full name
134
+ * 5. `displayValue`
135
+ * 6. `number – title` / `number – name` / `number – subject`
136
+ * 7. `display`, `text`, `symbol`, `code`
137
+ *
138
+ * The result is cleaned (normalizes whitespace and punctuation spacing) and optionally
139
+ * truncated to `maxLength` characters. Returns the record `id` when `allowUseRecordId`
140
+ * is true and no other value could be resolved.
141
+ */
99
142
  const getRecordDisplay = (record, settings) => {
100
143
  if (!(0, core_1.isObject)(record))
101
144
  return "";
@@ -119,7 +162,9 @@ const getRecordDisplay = (record, settings) => {
119
162
  if (!(0, core_1.isNonEmptyString)(result, true) && (0, core_1.isNonEmptyString)(record.displayValue, true)) {
120
163
  result = record.displayValue;
121
164
  }
122
- const number = !(0, core_1.isNonEmptyString)(result, true) && (0, core_1.isNonEmptyString)(record.number, true) ? `${record.number} - ` : "";
165
+ const number = !(0, core_1.isNonEmptyString)(result, true) && (0, core_1.isNonEmptyString)(record.number, true)
166
+ ? `${record.number} - `
167
+ : "";
123
168
  if (!(0, core_1.isNonEmptyString)(result, true) && (0, core_1.isNonEmptyString)(record.title, true)) {
124
169
  result = `${number}${record.title}`;
125
170
  }
@@ -156,9 +201,21 @@ const getRecordDisplay = (record, settings) => {
156
201
  if ((0, lodash_1.isNumber)(maxLength) && result.length > maxLength) {
157
202
  result = `${result.substring(0, maxLength)} ...`;
158
203
  }
159
- return ((0, core_1.isNonEmptyString)(result, true) ? result : allowUseRecordId ? record.id : "") ?? (settings?.emptyReturnUndefined ? undefined : "");
204
+ return (((0, core_1.isNonEmptyString)(result, true) ? result : allowUseRecordId ? record.id : "") ??
205
+ (settings?.emptyReturnUndefined ? undefined : ""));
160
206
  };
161
207
  exports.getRecordDisplay = getRecordDisplay;
208
+ /**
209
+ * Resolves the best short abstract/description text for a record.
210
+ *
211
+ * Resolution priority:
212
+ * 1. Search highlight (`highlight.searchContent[0]`)
213
+ * 2. `summary`, `description`, `abstract`
214
+ * 3. `searchContent` (only when `settings.includeContent` is true)
215
+ *
216
+ * Whitespace and punctuation are normalized before returning.
217
+ * Truncates to `settings.maxLength` characters when provided.
218
+ */
162
219
  const getRecordAbstract = (record, settings) => {
163
220
  if (!(0, core_1.isObject)(record))
164
221
  return "";
@@ -172,7 +229,9 @@ const getRecordAbstract = (record, settings) => {
172
229
  result = record.description;
173
230
  if (!(0, core_1.isNonEmptyString)(result) && (0, core_1.isNonEmptyString)(record.abstract))
174
231
  result = record.abstract;
175
- if (settings?.includeContent && !(0, core_1.isNonEmptyString)(result) && (0, core_1.isNonEmptyString)(record.searchContent)) {
232
+ if (settings?.includeContent &&
233
+ !(0, core_1.isNonEmptyString)(result) &&
234
+ (0, core_1.isNonEmptyString)(record.searchContent)) {
176
235
  result = record.searchContent;
177
236
  }
178
237
  result = result
@@ -193,6 +252,10 @@ const getRecordAbstract = (record, settings) => {
193
252
  return result;
194
253
  };
195
254
  exports.getRecordAbstract = getRecordAbstract;
255
+ /**
256
+ * Returns the first non-empty string value found by iterating `contentFields` in order.
257
+ * Used internally by `getRecordContent` when an entity defines explicit content field names.
258
+ */
196
259
  const getRecordContentByContentFields = (record, contentFields) => {
197
260
  let result = "";
198
261
  if (contentFields && (0, lodash_1.isArray)(contentFields) && !(0, core_1.isNonEmptyString)(result)) {
@@ -203,6 +266,16 @@ const getRecordContentByContentFields = (record, contentFields) => {
203
266
  return result;
204
267
  };
205
268
  exports.getRecordContentByContentFields = getRecordContentByContentFields;
269
+ /**
270
+ * Resolves the main body content for a record, suitable for indexing or display.
271
+ *
272
+ * Resolution priority:
273
+ * 1. Search highlight (`_ui.highlight.content`)
274
+ * 2. `searchContent` (when `includeSearchDisplay` is true)
275
+ * 3. Entity/settings `contentFields` (defaults to description, note, detail, content, …)
276
+ *
277
+ * Truncates to `maxLength` when provided.
278
+ */
206
279
  const getRecordContent = (record, settings) => {
207
280
  if (!(0, core_1.isObject)(record))
208
281
  return "";
@@ -210,7 +283,21 @@ const getRecordContent = (record, settings) => {
210
283
  const includeSearchDisplay = settings?.includeSearchDisplay == true;
211
284
  const maxLength = settings?.maxLength;
212
285
  const entity = settings?.entity;
213
- const contentFields = settings?.contentFields ?? entity?.contentFields ?? ["description", "note", "detail", "content", "summary", "introduction", "abstract", "title", "subject", "firstName", "lastName", "name"];
286
+ const contentFields = settings?.contentFields ??
287
+ entity?.contentFields ?? [
288
+ "description",
289
+ "note",
290
+ "detail",
291
+ "content",
292
+ "summary",
293
+ "introduction",
294
+ "abstract",
295
+ "title",
296
+ "subject",
297
+ "firstName",
298
+ "lastName",
299
+ "name"
300
+ ];
214
301
  if ((0, core_1.isNonEmptyString)(record._ui?.highlight?.content)) {
215
302
  result = record._ui?.highlight?.content;
216
303
  }
@@ -223,24 +310,41 @@ const getRecordContent = (record, settings) => {
223
310
  if ((0, lodash_1.isNumber)(maxLength) && (0, core_1.isNonEmptyString)(result, true) && result.length > maxLength) {
224
311
  result = `${result.substring(0, maxLength)} ...`;
225
312
  }
226
- return (0, core_1.isNonEmptyString)(result, true) ? result.trim() : settings?.emptyReturnUndefined ? undefined : "";
313
+ return (0, core_1.isNonEmptyString)(result, true)
314
+ ? result.trim()
315
+ : settings?.emptyReturnUndefined
316
+ ? undefined
317
+ : "";
227
318
  };
228
319
  exports.getRecordContent = getRecordContent;
320
+ /**
321
+ * Formats a complete postal address from prefixed address fields on a record
322
+ * (e.g. `prefix = "billing"` reads `billingLine1`, `billingCity`, `billingState`, …).
323
+ * Returns `""` when the record does not contain the minimum required fields
324
+ * (city + state + country, or postalCode + country).
325
+ */
229
326
  const getRecordAddress = (record, prefix) => {
230
327
  if (!(0, core_1.isObject)(record))
231
328
  return "";
232
329
  const line1 = (0, core_1.isNonEmptyString)(record[`${prefix}Line1`]) ? record[`${prefix}Line1`] : "";
233
330
  const line2 = (0, core_1.isNonEmptyString)(record[`${prefix}Line2`]) ? record[`${prefix}Line2`] : "";
234
331
  const city = (0, core_1.isNonEmptyString)(record[`${prefix}City`]) ? record[`${prefix}City`] : "";
235
- const postalCode = (0, core_1.isNonEmptyString)(record[`${prefix}PostalCode`]) ? record[`${prefix}PostalCode`] : "";
332
+ const postalCode = (0, core_1.isNonEmptyString)(record[`${prefix}PostalCode`])
333
+ ? record[`${prefix}PostalCode`]
334
+ : "";
236
335
  const state = (0, core_1.isNonEmptyString)(record[`${prefix}State`]) ? record[`${prefix}State`] : "";
237
336
  const country = (0, core_1.isNonEmptyString)(record[`${prefix}Country`]) ? record[`${prefix}Country`] : "";
238
- if ((line1.length > 0 && city.length > 0 && state.length > 0 && country.length > 0) || (postalCode.length > 0 && country.length > 0)) {
337
+ if ((line1.length > 0 && city.length > 0 && state.length > 0 && country.length > 0) ||
338
+ (postalCode.length > 0 && country.length > 0)) {
239
339
  return `${line1} ${line2},${city},${state},${postalCode},${country}`;
240
340
  }
241
341
  return "";
242
342
  };
243
343
  exports.getRecordAddress = getRecordAddress;
344
+ /**
345
+ * Returns the primary media URL for a record, trying `media`, `photoUrl`, and
346
+ * `source.photoUrl` in that order. Returns `""` when none are set.
347
+ */
244
348
  const getRecordMedia = (record) => {
245
349
  if (!(0, core_1.isObject)(record))
246
350
  return "";
@@ -253,6 +357,14 @@ const getRecordMedia = (record) => {
253
357
  return "";
254
358
  };
255
359
  exports.getRecordMedia = getRecordMedia;
360
+ /**
361
+ * Constructs a display name for a user-like record.
362
+ *
363
+ * Resolution order: `fullName` → `firstName + lastName` → `name` → `email`.
364
+ * `skipFullNameProp`, `skipNameProp`, `skipEmailProp` let callers exclude specific steps.
365
+ * Simplified Chinese locale (Windows locale code 2052) has spaces stripped from the assembled name.
366
+ * Multiple consecutive spaces are collapsed to a single space.
367
+ */
256
368
  const getRecordFullName = (user, settings) => {
257
369
  const { skipFullNameProp, skipNameProp, skipEmailProp } = settings ?? {};
258
370
  if (!(0, core_1.isObject)(user))
@@ -262,7 +374,9 @@ const getRecordFullName = (user, settings) => {
262
374
  let fullName = (0, core_1.isNonEmptyString)(user.fullName) && !skipFullNameProp ? user.fullName.trim() : "";
263
375
  const firstName = (0, core_1.isNonEmptyString)(user.firstName) ? user.firstName.trim() : "";
264
376
  const lastName = (0, core_1.isNonEmptyString)(user.lastName) ? user.lastName.trim() : "";
265
- if (fullName.length == 0 || (firstName.length > 0 && lastName.length > 0) || (fullName.length > 0 && fullName == email)) {
377
+ if (fullName.length == 0 ||
378
+ (firstName.length > 0 && lastName.length > 0) ||
379
+ (fullName.length > 0 && fullName == email)) {
266
380
  fullName = `${firstName} ${lastName.length > 0 ? " " + lastName : ""}`.trim();
267
381
  }
268
382
  if (user.lcid === 2052)
@@ -275,6 +389,12 @@ const getRecordFullName = (user, settings) => {
275
389
  return fullName;
276
390
  };
277
391
  exports.getRecordFullName = getRecordFullName;
392
+ /**
393
+ * Returns a formatted email address string for a record that has a valid `email` field.
394
+ * Format: `"Full Name <email@example.com>"`, or just `"email@example.com"` when
395
+ * `emailOnly` is true or no display name is available.
396
+ * Returns `undefined` when the record has no valid email.
397
+ */
278
398
  const getRecordEmailAddress = (record, emailOnly) => {
279
399
  if ((0, core_1.isObject)(record) && (0, core_1.isEmail)(record.email)) {
280
400
  const fullName = (0, exports.getRecordFullName)(record);
@@ -288,6 +408,12 @@ const getRecordEmailAddress = (record, emailOnly) => {
288
408
  return undefined;
289
409
  };
290
410
  exports.getRecordEmailAddress = getRecordEmailAddress;
411
+ /**
412
+ * Builds an Open Graph / page metadata object for a record, suitable for `<head>` tags.
413
+ * Returns `{ id, title, description, type: "article", image? }`.
414
+ * Title is derived from `getRecordDisplay` (or `getRecordAbstract` when display is empty),
415
+ * optionally prefixed with `settings.siteName`.
416
+ */
291
417
  const getRecordPageMetadata = (record, settings) => {
292
418
  const result = {};
293
419
  const recordDisplay = (0, exports.getRecordDisplay)(record);
@@ -295,12 +421,15 @@ const getRecordPageMetadata = (record, settings) => {
295
421
  result.title =
296
422
  (0, core_1.isObject)(settings) && settings?.siteName
297
423
  ? `${settings?.siteName} - ${(0, exports.getRecordAbstract)(record, {
298
- maxLength: 64,
424
+ maxLength: 64
299
425
  })}`
300
426
  : (0, exports.getRecordAbstract)(record, { maxLength: 64 });
301
427
  }
302
428
  else {
303
- result.title = (0, core_1.isObject)(settings) && settings?.siteName ? `${settings?.siteName} - ${recordDisplay}` : recordDisplay;
429
+ result.title =
430
+ (0, core_1.isObject)(settings) && settings?.siteName
431
+ ? `${settings?.siteName} - ${recordDisplay}`
432
+ : recordDisplay;
304
433
  }
305
434
  result.id = record.id;
306
435
  result.description = (0, exports.getRecordAbstract)(record, { maxLength: 128 });
@@ -311,6 +440,11 @@ const getRecordPageMetadata = (record, settings) => {
311
440
  return result;
312
441
  };
313
442
  exports.getRecordPageMetadata = getRecordPageMetadata;
443
+ /**
444
+ * Computes the current slug via `getRecordSlug` and ensures it is present in both
445
+ * `record.slug` (current) and `record.slugs` (historical list, deduplicated).
446
+ * Call this before persisting a record to keep slug history intact for redirects.
447
+ */
314
448
  const applyRecordSlug = (record) => {
315
449
  const curSlug = (0, exports.getRecordSlug)(record);
316
450
  if (curSlug) {
@@ -324,6 +458,13 @@ const applyRecordSlug = (record) => {
324
458
  return record;
325
459
  };
326
460
  exports.applyRecordSlug = applyRecordSlug;
461
+ /**
462
+ * Derives a URL slug for a record.
463
+ * Uses `record.customSlug` when set; otherwise derives from `getRecordDisplay`.
464
+ * The slug is truncated to 128 characters and suffixed with the second GUID segment
465
+ * of the record ID (e.g. `"my-post-e29b"`) to guarantee global uniqueness.
466
+ * Returns `undefined` when the record has no `id`.
467
+ */
327
468
  const getRecordSlug = (record) => {
328
469
  if (!record.id)
329
470
  return undefined;
@@ -337,12 +478,23 @@ const getRecordSlug = (record) => {
337
478
  return curSlug;
338
479
  };
339
480
  exports.getRecordSlug = getRecordSlug;
481
+ /**
482
+ * Builds the storage path for a file attached to a record.
483
+ * Format: `"{solutionId}/{organizationId}/{entityName}/{recordId}/{fieldName}"`.
484
+ * `fieldName` defaults to `"file"` when not provided.
485
+ */
340
486
  const getRecordFilePath = (props) => {
341
487
  const { solutionId, organizationId, recordId, entityName } = props;
342
488
  const fieldName = props.fieldName ?? "file";
343
489
  return `${solutionId}/${organizationId}/${entityName}/${recordId}/${fieldName}`;
344
490
  };
345
491
  exports.getRecordFilePath = getRecordFilePath;
492
+ /**
493
+ * Extracts an `IContext` from a record that carries context fields directly
494
+ * (e.g. an API request body that has been enriched with caller context).
495
+ * Picks `solutionId`, `domain`, `sourceIp`, `userId`, `organizationId`,
496
+ * `user`, `organization`, and `solution`.
497
+ */
346
498
  const getContextFromRecord = (record) => {
347
499
  const { solutionId, domain, sourceIp, userId, organizationId, user, organization, solution } = record;
348
500
  return {
@@ -353,19 +505,32 @@ const getContextFromRecord = (record) => {
353
505
  organizationId,
354
506
  user,
355
507
  organization,
356
- solution,
508
+ solution
357
509
  };
358
510
  };
359
511
  exports.getContextFromRecord = getContextFromRecord;
512
+ /**
513
+ * Returns true when a record is marked global but belongs to a different organization
514
+ * than the one in the current context. Used to identify marketplace/shared content.
515
+ */
360
516
  const fromOtherOrganization = (context, record) => {
361
517
  return record.isGlobal && record.organizationId != context.organizationId;
362
518
  };
363
519
  exports.fromOtherOrganization = fromOtherOrganization;
520
+ /**
521
+ * Returns true when the record's ID appears in the current user's favorites list
522
+ * for the record's entity type (`context.user.favorites[entityName]`).
523
+ */
364
524
  const isFavorite = (context, record) => {
365
525
  const favorites = context.user?.favorites ? context.user?.favorites[record.entityName] : [];
366
526
  return favorites.includes(record.id);
367
527
  };
368
528
  exports.isFavorite = isFavorite;
529
+ /**
530
+ * Returns true when a record is global and its owning organization differs from the
531
+ * current context's organization. Equivalent to `fromOtherOrganization` but compares
532
+ * against `context.organization.id` instead of `context.organizationId`.
533
+ */
369
534
  const isFromMarketplace = (context, record) => {
370
535
  return record.isGlobal && record.organizationId != context?.organization?.id;
371
536
  };