@memberjunction/core-actions 5.23.0 → 5.25.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 (78) hide show
  1. package/dist/custom/files/add-document-content.action.d.ts +20 -0
  2. package/dist/custom/files/add-document-content.action.d.ts.map +1 -0
  3. package/dist/custom/files/add-document-content.action.js +74 -0
  4. package/dist/custom/files/add-document-content.action.js.map +1 -0
  5. package/dist/custom/files/base-file-storage.action.d.ts +6 -9
  6. package/dist/custom/files/base-file-storage.action.d.ts.map +1 -1
  7. package/dist/custom/files/base-file-storage.action.js +14 -39
  8. package/dist/custom/files/base-file-storage.action.js.map +1 -1
  9. package/dist/custom/files/create-document.action.d.ts +19 -0
  10. package/dist/custom/files/create-document.action.d.ts.map +1 -0
  11. package/dist/custom/files/create-document.action.js +65 -0
  12. package/dist/custom/files/create-document.action.js.map +1 -0
  13. package/dist/custom/files/excel-reader.action.js +1 -1
  14. package/dist/custom/files/excel-reader.action.js.map +1 -1
  15. package/dist/custom/files/finalize-document.action.d.ts +21 -0
  16. package/dist/custom/files/finalize-document.action.d.ts.map +1 -0
  17. package/dist/custom/files/finalize-document.action.js +94 -0
  18. package/dist/custom/files/finalize-document.action.js.map +1 -0
  19. package/dist/custom/files/index.d.ts +5 -0
  20. package/dist/custom/files/index.d.ts.map +1 -1
  21. package/dist/custom/files/index.js +6 -0
  22. package/dist/custom/files/index.js.map +1 -1
  23. package/dist/custom/files/list-storage-providers.action.d.ts.map +1 -1
  24. package/dist/custom/files/list-storage-providers.action.js +6 -33
  25. package/dist/custom/files/list-storage-providers.action.js.map +1 -1
  26. package/dist/custom/files/modify-document-section.action.d.ts +19 -0
  27. package/dist/custom/files/modify-document-section.action.d.ts.map +1 -0
  28. package/dist/custom/files/modify-document-section.action.js +85 -0
  29. package/dist/custom/files/modify-document-section.action.js.map +1 -0
  30. package/dist/custom/files/pdf-extractor.action.d.ts +8 -14
  31. package/dist/custom/files/pdf-extractor.action.d.ts.map +1 -1
  32. package/dist/custom/files/pdf-extractor.action.js +89 -91
  33. package/dist/custom/files/pdf-extractor.action.js.map +1 -1
  34. package/dist/custom/files/preview-document.action.d.ts +17 -0
  35. package/dist/custom/files/preview-document.action.d.ts.map +1 -0
  36. package/dist/custom/files/preview-document.action.js +50 -0
  37. package/dist/custom/files/preview-document.action.js.map +1 -0
  38. package/dist/custom/geo/scheduled-geocoding.action.d.ts +135 -0
  39. package/dist/custom/geo/scheduled-geocoding.action.d.ts.map +1 -0
  40. package/dist/custom/geo/scheduled-geocoding.action.js +428 -0
  41. package/dist/custom/geo/scheduled-geocoding.action.js.map +1 -0
  42. package/dist/custom/search/search.action.d.ts +67 -0
  43. package/dist/custom/search/search.action.d.ts.map +1 -0
  44. package/dist/custom/search/search.action.js +211 -0
  45. package/dist/custom/search/search.action.js.map +1 -0
  46. package/dist/custom/utilities/artifact-builder-service.d.ts +139 -0
  47. package/dist/custom/utilities/artifact-builder-service.d.ts.map +1 -0
  48. package/dist/custom/utilities/artifact-builder-service.js +338 -0
  49. package/dist/custom/utilities/artifact-builder-service.js.map +1 -0
  50. package/dist/custom/utilities/base-file-handler.d.ts +25 -3
  51. package/dist/custom/utilities/base-file-handler.d.ts.map +1 -1
  52. package/dist/custom/utilities/base-file-handler.js +81 -28
  53. package/dist/custom/utilities/base-file-handler.js.map +1 -1
  54. package/dist/custom/utilities/docx-renderer.d.ts +61 -0
  55. package/dist/custom/utilities/docx-renderer.d.ts.map +1 -0
  56. package/dist/custom/utilities/docx-renderer.js +409 -0
  57. package/dist/custom/utilities/docx-renderer.js.map +1 -0
  58. package/dist/custom/utilities/pdf-renderer.d.ts +53 -0
  59. package/dist/custom/utilities/pdf-renderer.d.ts.map +1 -0
  60. package/dist/custom/utilities/pdf-renderer.js +315 -0
  61. package/dist/custom/utilities/pdf-renderer.js.map +1 -0
  62. package/dist/custom/utilities/xlsx-renderer.d.ts +120 -0
  63. package/dist/custom/utilities/xlsx-renderer.d.ts.map +1 -0
  64. package/dist/custom/utilities/xlsx-renderer.js +165 -0
  65. package/dist/custom/utilities/xlsx-renderer.js.map +1 -0
  66. package/dist/index.d.ts +2 -2
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +3 -2
  69. package/dist/index.js.map +1 -1
  70. package/package.json +31 -27
  71. package/dist/custom/files/excel-writer.action.d.ts +0 -88
  72. package/dist/custom/files/excel-writer.action.d.ts.map +0 -1
  73. package/dist/custom/files/excel-writer.action.js +0 -326
  74. package/dist/custom/files/excel-writer.action.js.map +0 -1
  75. package/dist/custom/files/pdf-generator.action.d.ts +0 -66
  76. package/dist/custom/files/pdf-generator.action.d.ts.map +0 -1
  77. package/dist/custom/files/pdf-generator.action.js +0 -254
  78. package/dist/custom/files/pdf-generator.action.js.map +0 -1
@@ -0,0 +1,135 @@
1
+ import { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
2
+ import { BaseAction } from '@memberjunction/actions';
3
+ /**
4
+ * Scheduled geocoding maintenance action that handles three tasks:
5
+ *
6
+ * 1. **Missing records** — Finds records in geo-enabled entities that have
7
+ * non-null geo fields but no RecordGeoCode row, and geocodes them.
8
+ * 2. **Failed retries** — Retries RecordGeoCode rows with Status='failed'
9
+ * up to a configurable max retry count.
10
+ * 3. **Orphan cleanup** — Removes RecordGeoCode rows whose source entity
11
+ * record no longer exists.
12
+ *
13
+ * ## Parameters
14
+ * - **BatchSize** (default 10) — Number of records to geocode **concurrently**
15
+ * in each parallel batch. Controls API rate pressure. Google Geocoding API
16
+ * allows 50 QPS, so 10 is conservative and leaves headroom.
17
+ * - **MaxTotalRecords** (default unlimited) — Optional hard cap on the total
18
+ * number of records processed in a single run. When null/0, processes all
19
+ * pending records until exhausted.
20
+ * - **MaxRetries** (default 5) — Maximum retry count for failed geocoding
21
+ * attempts before a record is considered permanently failed.
22
+ *
23
+ * ## Cache Invalidation
24
+ * After geocoding each record, the action loads the parent entity record
25
+ * and fires a synthetic BaseEntity 'save' event so that any cached RunView
26
+ * results containing stale lat/lng (from the RecordGeoCode JOIN) are
27
+ * invalidated. This ensures the server cache stays consistent.
28
+ *
29
+ * Designed to run on a schedule (every few hours) as a safety net for records
30
+ * that bypass BaseEntity.Save() (bulk SQL imports, direct DB operations).
31
+ */
32
+ export declare class ScheduledGeocodingAction extends BaseAction {
33
+ private static readonly DEFAULT_MAX_RETRIES;
34
+ private static readonly DEFAULT_BATCH_SIZE;
35
+ protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
36
+ /**
37
+ * Find records in geo-enabled entities that have no RecordGeoCode row
38
+ * and geocode them. Processes ALL missing records (or up to maxTotal),
39
+ * geocoding in parallel batches of batchSize.
40
+ */
41
+ private processMissingRecords;
42
+ /**
43
+ * Process all missing RecordGeoCode rows for a single entity.
44
+ * Loads all entity records with non-null geo fields, filters out those
45
+ * that already have a RecordGeoCode row, then geocodes the rest in
46
+ * parallel batches.
47
+ *
48
+ * @param entityInfo - The entity to process
49
+ * @param contextUser - User context for data operations
50
+ * @param batchSize - Number of concurrent geocoding operations per batch
51
+ * @param maxRows - Maximum records to process (0 = unlimited)
52
+ */
53
+ private processMissingForEntity;
54
+ /**
55
+ * Retry failed geocoding attempts. Loads the source entity record
56
+ * and re-runs GeoCodeSyncService. Processes all eligible failures
57
+ * (or up to maxTotal) in parallel batches.
58
+ */
59
+ private processFailedRetries;
60
+ /**
61
+ * Load source entity records for a set of RecordGeoCode rows.
62
+ * Skips records whose source entity no longer exists.
63
+ */
64
+ private loadSourceEntities;
65
+ /**
66
+ * Remove RecordGeoCode rows whose source entity record no longer exists.
67
+ * Processes one entity at a time to keep queries efficient.
68
+ */
69
+ private cleanupOrphanedRecords;
70
+ /**
71
+ * Geocode a list of entity records in parallel batches, then fire cache
72
+ * invalidation events for each successfully geocoded record.
73
+ *
74
+ * For each record in the batch:
75
+ * 1. Run GeoCodeSyncService.SyncIfChanged to geocode and update RecordGeoCode
76
+ * 2. Fire a synthetic BaseEntity 'save' event with the loaded entity so that
77
+ * LocalCacheManager invalidates any cached RunView results containing stale
78
+ * lat/lng from the RecordGeoCode JOIN
79
+ *
80
+ * @param entities - Pre-loaded BaseEntity instances to geocode
81
+ * @param entityInfo - Entity metadata
82
+ * @param contextUser - User context
83
+ * @param batchSize - Number of concurrent geocoding operations per batch
84
+ */
85
+ private geocodeBatch;
86
+ /**
87
+ * Geocode a single entity record and fire a cache invalidation event.
88
+ *
89
+ * After geocoding updates the RecordGeoCode row, we need to tell the
90
+ * cache system that this entity record's view data has changed (because
91
+ * the view JOINs to RecordGeoCode for __mj_Latitude/__mj_Longitude).
92
+ * We do this by firing a synthetic BaseEntity 'save' event with the
93
+ * entity instance — the same event that BaseEntity.Save() fires.
94
+ *
95
+ * @returns true if geocoding succeeded, false otherwise
96
+ */
97
+ private geocodeAndInvalidate;
98
+ /**
99
+ * Fire a synthetic BaseEntity 'save' event via MJGlobal so that
100
+ * LocalCacheManager and BaseEngine listeners pick up the change.
101
+ *
102
+ * This is the same event that BaseEntity.RaiseEvent() fires after
103
+ * a successful Save(), but triggered externally because the record's
104
+ * view data changed due to a RecordGeoCode update (not a direct save).
105
+ *
106
+ * The entity instance is passed as both `component` and `event.baseEntity`
107
+ * so cache handlers can read EntityInfo, PrimaryKeys, and GetAll() —
108
+ * exactly the same contract as a real save event.
109
+ */
110
+ private fireSyntheticSaveEvent;
111
+ /**
112
+ * Get the geo address fields (non-lat/lng) for an entity.
113
+ * These are the fields that, when non-null, indicate a record
114
+ * should have a geocode.
115
+ */
116
+ private getGeoAddressFields;
117
+ /**
118
+ * Get the set of RecordIDs that already have a RecordGeoCode row for an entity.
119
+ */
120
+ private getExistingGeoCodeRecordIds;
121
+ /**
122
+ * Build a RecordID string from an entity's primary key, matching the
123
+ * format used in the view's LEFT JOIN to RecordGeoCode.
124
+ */
125
+ private buildRecordId;
126
+ /**
127
+ * Extract a numeric parameter with a default value.
128
+ */
129
+ private getNumericParam;
130
+ /**
131
+ * Extract an optional numeric parameter. Returns null if not provided.
132
+ */
133
+ private getNullableNumericParam;
134
+ }
135
+ //# sourceMappingURL=scheduled-geocoding.action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled-geocoding.action.d.ts","sourceRoot":"","sources":["../../../src/custom/geo/scheduled-geocoding.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AASrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBACa,wBAAyB,SAAQ,UAAU;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAM;cAEhC,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyCvF;;;;OAIG;YACW,qBAAqB;IA0BnC;;;;;;;;;;OAUG;YACW,uBAAuB;IAyDrC;;;;OAIG;YACW,oBAAoB;IA2DlC;;;OAGG;YACW,kBAAkB;IA4BhC;;;OAGG;YACW,sBAAsB;IA0DpC;;;;;;;;;;;;;;OAcG;YACW,YAAY;IA0B1B;;;;;;;;;;OAUG;YACW,oBAAoB;IAqBlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAmB9B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;YACW,2BAA2B;IAezC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAMlC"}
@@ -0,0 +1,428 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var ScheduledGeocodingAction_1;
8
+ import { BaseAction } from '@memberjunction/actions';
9
+ import { RegisterClass, MJGlobal, MJEventType } from '@memberjunction/global';
10
+ import { RunView, Metadata, LogStatus, LogError, CompositeKey, BaseEntity, BaseEntityEvent } from '@memberjunction/core';
11
+ import { GeoCodeSyncService } from '@memberjunction/geo-core';
12
+ /**
13
+ * Scheduled geocoding maintenance action that handles three tasks:
14
+ *
15
+ * 1. **Missing records** — Finds records in geo-enabled entities that have
16
+ * non-null geo fields but no RecordGeoCode row, and geocodes them.
17
+ * 2. **Failed retries** — Retries RecordGeoCode rows with Status='failed'
18
+ * up to a configurable max retry count.
19
+ * 3. **Orphan cleanup** — Removes RecordGeoCode rows whose source entity
20
+ * record no longer exists.
21
+ *
22
+ * ## Parameters
23
+ * - **BatchSize** (default 10) — Number of records to geocode **concurrently**
24
+ * in each parallel batch. Controls API rate pressure. Google Geocoding API
25
+ * allows 50 QPS, so 10 is conservative and leaves headroom.
26
+ * - **MaxTotalRecords** (default unlimited) — Optional hard cap on the total
27
+ * number of records processed in a single run. When null/0, processes all
28
+ * pending records until exhausted.
29
+ * - **MaxRetries** (default 5) — Maximum retry count for failed geocoding
30
+ * attempts before a record is considered permanently failed.
31
+ *
32
+ * ## Cache Invalidation
33
+ * After geocoding each record, the action loads the parent entity record
34
+ * and fires a synthetic BaseEntity 'save' event so that any cached RunView
35
+ * results containing stale lat/lng (from the RecordGeoCode JOIN) are
36
+ * invalidated. This ensures the server cache stays consistent.
37
+ *
38
+ * Designed to run on a schedule (every few hours) as a safety net for records
39
+ * that bypass BaseEntity.Save() (bulk SQL imports, direct DB operations).
40
+ */
41
+ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction {
42
+ static { ScheduledGeocodingAction_1 = this; }
43
+ static { this.DEFAULT_MAX_RETRIES = 5; }
44
+ static { this.DEFAULT_BATCH_SIZE = 10; }
45
+ async InternalRunAction(params) {
46
+ const contextUser = params.ContextUser;
47
+ if (!contextUser) {
48
+ return { Success: false, ResultCode: 'MISSING_USER', Message: 'ScheduledGeocodingAction requires a context user' };
49
+ }
50
+ const maxRetries = this.getNumericParam(params, 'MaxRetries', ScheduledGeocodingAction_1.DEFAULT_MAX_RETRIES);
51
+ const batchSize = this.getNumericParam(params, 'BatchSize', ScheduledGeocodingAction_1.DEFAULT_BATCH_SIZE);
52
+ const maxTotal = this.getNullableNumericParam(params, 'MaxTotalRecords');
53
+ LogStatus(`ScheduledGeocodingAction: Starting maintenance run (BatchSize=${batchSize}, MaxTotal=${maxTotal ?? 'unlimited'})`);
54
+ const stats = { MissingProcessed: 0, MissingSuccess: 0, RetriesProcessed: 0, RetriesSuccess: 0, OrphansRemoved: 0 };
55
+ // Step 1: Find and geocode missing records
56
+ const missingStats = await this.processMissingRecords(contextUser, batchSize, maxTotal);
57
+ stats.MissingProcessed = missingStats.Processed;
58
+ stats.MissingSuccess = missingStats.Success;
59
+ // Step 2: Retry failed geocoding attempts
60
+ const retryMaxTotal = maxTotal != null ? Math.max(0, maxTotal - stats.MissingProcessed) : null;
61
+ const retryStats = await this.processFailedRetries(contextUser, maxRetries, batchSize, retryMaxTotal);
62
+ stats.RetriesProcessed = retryStats.Processed;
63
+ stats.RetriesSuccess = retryStats.Success;
64
+ // Step 3: Clean up orphaned RecordGeoCode rows
65
+ stats.OrphansRemoved = await this.cleanupOrphanedRecords(contextUser, batchSize);
66
+ LogStatus(`ScheduledGeocodingAction: Complete — Missing: ${stats.MissingProcessed} processed (${stats.MissingSuccess} success), Retries: ${stats.RetriesProcessed} (${stats.RetriesSuccess} success), Orphans: ${stats.OrphansRemoved} removed`);
67
+ return {
68
+ Success: true,
69
+ ResultCode: 'SUCCESS',
70
+ Message: JSON.stringify(stats)
71
+ };
72
+ }
73
+ // ================================================================
74
+ // Step 1: Process missing RecordGeoCode rows
75
+ // ================================================================
76
+ /**
77
+ * Find records in geo-enabled entities that have no RecordGeoCode row
78
+ * and geocode them. Processes ALL missing records (or up to maxTotal),
79
+ * geocoding in parallel batches of batchSize.
80
+ */
81
+ async processMissingRecords(contextUser, batchSize, maxTotal) {
82
+ const md = new Metadata();
83
+ const geoEntities = md.Entities.filter(e => e.SupportsGeoCoding);
84
+ let totalProcessed = 0;
85
+ let totalSuccess = 0;
86
+ for (const entityInfo of geoEntities) {
87
+ if (maxTotal != null && totalProcessed >= maxTotal)
88
+ break;
89
+ const remaining = maxTotal != null ? maxTotal - totalProcessed : null;
90
+ const entityStats = await this.processMissingForEntity(entityInfo, contextUser, batchSize, remaining);
91
+ totalProcessed += entityStats.Processed;
92
+ totalSuccess += entityStats.Success;
93
+ }
94
+ if (totalProcessed > 0) {
95
+ LogStatus(`ScheduledGeocodingAction: Processed ${totalProcessed} missing records (${totalSuccess} geocoded successfully)`);
96
+ }
97
+ return { Processed: totalProcessed, Success: totalSuccess };
98
+ }
99
+ /**
100
+ * Process all missing RecordGeoCode rows for a single entity.
101
+ * Loads all entity records with non-null geo fields, filters out those
102
+ * that already have a RecordGeoCode row, then geocodes the rest in
103
+ * parallel batches.
104
+ *
105
+ * @param entityInfo - The entity to process
106
+ * @param contextUser - User context for data operations
107
+ * @param batchSize - Number of concurrent geocoding operations per batch
108
+ * @param maxRows - Maximum records to process (0 = unlimited)
109
+ */
110
+ async processMissingForEntity(entityInfo, contextUser, batchSize, maxRows) {
111
+ const rv = new RunView();
112
+ const pkField = entityInfo.FirstPrimaryKey;
113
+ if (!pkField)
114
+ return { Processed: 0, Success: 0 };
115
+ const geoFields = this.getGeoAddressFields(entityInfo);
116
+ if (geoFields.length === 0)
117
+ return { Processed: 0, Success: 0 };
118
+ try {
119
+ // Get all record IDs that already have a RecordGeoCode row
120
+ const existingRecordIds = await this.getExistingGeoCodeRecordIds(entityInfo.ID, contextUser);
121
+ // Load entity records with non-null geo fields
122
+ const nonNullConditions = geoFields.map(f => `${f.Name} IS NOT NULL`).join(' OR ');
123
+ const result = await rv.RunView({
124
+ EntityName: entityInfo.Name,
125
+ ExtraFilter: `(${nonNullConditions})`,
126
+ IgnoreMaxRows: true,
127
+ BypassCache: true,
128
+ ResultType: 'entity_object'
129
+ }, contextUser);
130
+ if (!result.Success || result.Results.length === 0) {
131
+ return { Processed: 0, Success: 0 };
132
+ }
133
+ // Filter to records missing geocodes
134
+ let missingRecords = result.Results
135
+ .filter(entity => {
136
+ const recordId = this.buildRecordId(entity);
137
+ return !existingRecordIds.has(recordId);
138
+ });
139
+ if (maxRows != null) {
140
+ missingRecords = missingRecords.slice(0, maxRows);
141
+ }
142
+ if (missingRecords.length === 0)
143
+ return { Processed: 0, Success: 0 };
144
+ // Geocode in parallel batches
145
+ return await this.geocodeBatch(missingRecords, entityInfo, contextUser, batchSize);
146
+ }
147
+ catch (e) {
148
+ const msg = e instanceof Error ? e.message : String(e);
149
+ LogError(`ScheduledGeocodingAction: Error querying missing records for ${entityInfo.Name}: ${msg}`);
150
+ return { Processed: 0, Success: 0 };
151
+ }
152
+ }
153
+ // ================================================================
154
+ // Step 2: Retry failed geocoding attempts
155
+ // ================================================================
156
+ /**
157
+ * Retry failed geocoding attempts. Loads the source entity record
158
+ * and re-runs GeoCodeSyncService. Processes all eligible failures
159
+ * (or up to maxTotal) in parallel batches.
160
+ */
161
+ async processFailedRetries(contextUser, maxRetries, batchSize, maxTotal) {
162
+ const rv = new RunView();
163
+ const failedResult = await rv.RunView({
164
+ EntityName: 'MJ: Record Geo Codes',
165
+ ExtraFilter: `Status='failed' AND RetryCount < ${maxRetries}`,
166
+ IgnoreMaxRows: true,
167
+ OrderBy: 'RetryCount ASC, GeocodedAt ASC',
168
+ ResultType: 'entity_object'
169
+ }, contextUser);
170
+ if (!failedResult.Success || failedResult.Results.length === 0) {
171
+ return { Processed: 0, Success: 0 };
172
+ }
173
+ let records = failedResult.Results;
174
+ if (maxTotal != null) {
175
+ records = records.slice(0, maxTotal);
176
+ }
177
+ LogStatus(`ScheduledGeocodingAction: ${records.length} failed records eligible for retry`);
178
+ const md = new Metadata();
179
+ let totalProcessed = 0;
180
+ let totalSuccess = 0;
181
+ // Group by entity for efficient processing
182
+ const byEntity = new Map();
183
+ for (const geoRecord of records) {
184
+ const key = geoRecord.EntityID;
185
+ let arr = byEntity.get(key);
186
+ if (!arr) {
187
+ arr = [];
188
+ byEntity.set(key, arr);
189
+ }
190
+ arr.push(geoRecord);
191
+ }
192
+ for (const [entityId, geoRecords] of byEntity) {
193
+ const entityInfo = md.EntityByID(entityId);
194
+ if (!entityInfo)
195
+ continue;
196
+ // Load source entity records and geocode them in parallel batches
197
+ const entities = await this.loadSourceEntities(geoRecords, entityInfo, contextUser);
198
+ if (entities.length === 0)
199
+ continue;
200
+ const stats = await this.geocodeBatch(entities, entityInfo, contextUser, batchSize);
201
+ totalProcessed += stats.Processed;
202
+ totalSuccess += stats.Success;
203
+ }
204
+ return { Processed: totalProcessed, Success: totalSuccess };
205
+ }
206
+ /**
207
+ * Load source entity records for a set of RecordGeoCode rows.
208
+ * Skips records whose source entity no longer exists.
209
+ */
210
+ async loadSourceEntities(geoRecords, entityInfo, contextUser) {
211
+ const md = new Metadata();
212
+ const entities = [];
213
+ for (const geoRecord of geoRecords) {
214
+ try {
215
+ const entity = await md.GetEntityObject(entityInfo.Name, contextUser);
216
+ const pk = new CompositeKey([{ FieldName: 'ID', Value: geoRecord.RecordID }]);
217
+ const loaded = await entity.InnerLoad(pk);
218
+ if (loaded) {
219
+ entities.push(entity);
220
+ }
221
+ }
222
+ catch {
223
+ // Source record doesn't exist or can't be loaded — skip
224
+ }
225
+ }
226
+ return entities;
227
+ }
228
+ // ================================================================
229
+ // Step 3: Orphan cleanup
230
+ // ================================================================
231
+ /**
232
+ * Remove RecordGeoCode rows whose source entity record no longer exists.
233
+ * Processes one entity at a time to keep queries efficient.
234
+ */
235
+ async cleanupOrphanedRecords(contextUser, batchSize) {
236
+ const rv = new RunView();
237
+ const md = new Metadata();
238
+ let totalRemoved = 0;
239
+ // Get distinct EntityIDs from RecordGeoCode
240
+ const entityResult = await rv.RunView({
241
+ EntityName: 'MJ: Record Geo Codes',
242
+ Fields: ['EntityID'],
243
+ ResultType: 'simple',
244
+ IgnoreMaxRows: true
245
+ }, contextUser);
246
+ if (!entityResult.Success)
247
+ return 0;
248
+ const entityIds = [...new Set(entityResult.Results.map(r => r.EntityID))];
249
+ for (const entityId of entityIds) {
250
+ try {
251
+ const entityInfo = md.EntityByID(entityId);
252
+ if (!entityInfo)
253
+ continue;
254
+ const pkField = entityInfo.FirstPrimaryKey;
255
+ if (!pkField)
256
+ continue;
257
+ const orphanResult = await rv.RunView({
258
+ EntityName: 'MJ: Record Geo Codes',
259
+ ExtraFilter: `EntityID = '${entityId}' AND NOT EXISTS (SELECT 1 FROM ${entityInfo.SchemaName}.${entityInfo.BaseTable} src WHERE CAST(src.${pkField.Name} AS NVARCHAR(450)) = RecordID)`,
260
+ IgnoreMaxRows: true,
261
+ ResultType: 'entity_object'
262
+ }, contextUser);
263
+ if (orphanResult.Success && orphanResult.Results.length > 0) {
264
+ // Delete orphans in parallel batches
265
+ const orphans = orphanResult.Results;
266
+ for (let i = 0; i < orphans.length; i += batchSize) {
267
+ const batch = orphans.slice(i, i + batchSize);
268
+ const results = await Promise.allSettled(batch.map(o => o.Delete()));
269
+ totalRemoved += results.filter(r => r.status === 'fulfilled' && r.value).length;
270
+ }
271
+ LogStatus(`ScheduledGeocodingAction: Removed ${orphanResult.Results.length} orphaned geo records for ${entityInfo.Name}`);
272
+ }
273
+ }
274
+ catch (e) {
275
+ const msg = e instanceof Error ? e.message : String(e);
276
+ LogError(`ScheduledGeocodingAction: Orphan cleanup error for entity ${entityId}: ${msg}`);
277
+ }
278
+ }
279
+ return totalRemoved;
280
+ }
281
+ // ================================================================
282
+ // Core: parallel batch geocoding with cache invalidation
283
+ // ================================================================
284
+ /**
285
+ * Geocode a list of entity records in parallel batches, then fire cache
286
+ * invalidation events for each successfully geocoded record.
287
+ *
288
+ * For each record in the batch:
289
+ * 1. Run GeoCodeSyncService.SyncIfChanged to geocode and update RecordGeoCode
290
+ * 2. Fire a synthetic BaseEntity 'save' event with the loaded entity so that
291
+ * LocalCacheManager invalidates any cached RunView results containing stale
292
+ * lat/lng from the RecordGeoCode JOIN
293
+ *
294
+ * @param entities - Pre-loaded BaseEntity instances to geocode
295
+ * @param entityInfo - Entity metadata
296
+ * @param contextUser - User context
297
+ * @param batchSize - Number of concurrent geocoding operations per batch
298
+ */
299
+ async geocodeBatch(entities, entityInfo, contextUser, batchSize) {
300
+ let totalSuccess = 0;
301
+ for (let i = 0; i < entities.length; i += batchSize) {
302
+ const batch = entities.slice(i, i + batchSize);
303
+ const results = await Promise.allSettled(batch.map(entity => this.geocodeAndInvalidate(entity, contextUser)));
304
+ const batchSuccess = results.filter(r => r.status === 'fulfilled' && r.value).length;
305
+ totalSuccess += batchSuccess;
306
+ if (entities.length > batchSize) {
307
+ LogStatus(`ScheduledGeocodingAction: ${entityInfo.Name} batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(entities.length / batchSize)} — ${batchSuccess}/${batch.length} success`);
308
+ }
309
+ }
310
+ return { Processed: entities.length, Success: totalSuccess };
311
+ }
312
+ /**
313
+ * Geocode a single entity record and fire a cache invalidation event.
314
+ *
315
+ * After geocoding updates the RecordGeoCode row, we need to tell the
316
+ * cache system that this entity record's view data has changed (because
317
+ * the view JOINs to RecordGeoCode for __mj_Latitude/__mj_Longitude).
318
+ * We do this by firing a synthetic BaseEntity 'save' event with the
319
+ * entity instance — the same event that BaseEntity.Save() fires.
320
+ *
321
+ * @returns true if geocoding succeeded, false otherwise
322
+ */
323
+ async geocodeAndInvalidate(entity, contextUser) {
324
+ try {
325
+ const result = await GeoCodeSyncService.Instance.SyncIfChanged(entity, contextUser);
326
+ if (result) {
327
+ // Geocoding produced new coordinates — fire cache invalidation.
328
+ // Reload the entity to pick up fresh data from the view (including
329
+ // the updated __mj_Latitude/__mj_Longitude from RecordGeoCode JOIN),
330
+ // then raise a save event so LocalCacheManager updates its caches.
331
+ await entity.InnerLoad(entity.PrimaryKey);
332
+ this.fireSyntheticSaveEvent(entity);
333
+ }
334
+ return true;
335
+ }
336
+ catch (e) {
337
+ const msg = e instanceof Error ? e.message : String(e);
338
+ LogError(`ScheduledGeocodingAction: Failed to geocode ${entity.EntityInfo.Name} record: ${msg}`);
339
+ return false;
340
+ }
341
+ }
342
+ /**
343
+ * Fire a synthetic BaseEntity 'save' event via MJGlobal so that
344
+ * LocalCacheManager and BaseEngine listeners pick up the change.
345
+ *
346
+ * This is the same event that BaseEntity.RaiseEvent() fires after
347
+ * a successful Save(), but triggered externally because the record's
348
+ * view data changed due to a RecordGeoCode update (not a direct save).
349
+ *
350
+ * The entity instance is passed as both `component` and `event.baseEntity`
351
+ * so cache handlers can read EntityInfo, PrimaryKeys, and GetAll() —
352
+ * exactly the same contract as a real save event.
353
+ */
354
+ fireSyntheticSaveEvent(entity) {
355
+ const event = new BaseEntityEvent();
356
+ event.type = 'save';
357
+ event.saveSubType = 'update';
358
+ event.baseEntity = entity;
359
+ event.payload = null;
360
+ MJGlobal.Instance.RaiseEvent({
361
+ component: entity,
362
+ event: MJEventType.ComponentEvent,
363
+ eventCode: BaseEntity.BaseEventCode,
364
+ args: event
365
+ });
366
+ }
367
+ // ================================================================
368
+ // Helpers
369
+ // ================================================================
370
+ /**
371
+ * Get the geo address fields (non-lat/lng) for an entity.
372
+ * These are the fields that, when non-null, indicate a record
373
+ * should have a geocode.
374
+ */
375
+ getGeoAddressFields(entityInfo) {
376
+ return entityInfo.Fields.filter(f => f.ExtendedType != null && f.ExtendedType.startsWith('Geo') &&
377
+ f.ExtendedType !== 'GeoLatitude' && f.ExtendedType !== 'GeoLongitude');
378
+ }
379
+ /**
380
+ * Get the set of RecordIDs that already have a RecordGeoCode row for an entity.
381
+ */
382
+ async getExistingGeoCodeRecordIds(entityId, contextUser) {
383
+ const rv = new RunView();
384
+ const existingResult = await rv.RunView({
385
+ EntityName: 'MJ: Record Geo Codes',
386
+ ExtraFilter: `EntityID = '${entityId}'`,
387
+ Fields: ['RecordID'],
388
+ ResultType: 'simple',
389
+ IgnoreMaxRows: true
390
+ }, contextUser);
391
+ return new Set(existingResult.Success ? existingResult.Results.map(r => r.RecordID) : []);
392
+ }
393
+ /**
394
+ * Build a RecordID string from an entity's primary key, matching the
395
+ * format used in the view's LEFT JOIN to RecordGeoCode.
396
+ */
397
+ buildRecordId(entity) {
398
+ const pkPairs = entity.PrimaryKey.KeyValuePairs;
399
+ return pkPairs.length === 1
400
+ ? String(pkPairs[0].Value)
401
+ : pkPairs.map(pk => String(pk.Value)).join('||');
402
+ }
403
+ /**
404
+ * Extract a numeric parameter with a default value.
405
+ */
406
+ getNumericParam(params, name, defaultValue) {
407
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === name.toLowerCase());
408
+ if (!param || param.Value === undefined || param.Value === null)
409
+ return defaultValue;
410
+ const parsed = Number(param.Value);
411
+ return isNaN(parsed) ? defaultValue : parsed;
412
+ }
413
+ /**
414
+ * Extract an optional numeric parameter. Returns null if not provided.
415
+ */
416
+ getNullableNumericParam(params, name) {
417
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === name.toLowerCase());
418
+ if (!param || param.Value === undefined || param.Value === null)
419
+ return null;
420
+ const parsed = Number(param.Value);
421
+ return isNaN(parsed) ? null : parsed;
422
+ }
423
+ };
424
+ ScheduledGeocodingAction = ScheduledGeocodingAction_1 = __decorate([
425
+ RegisterClass(BaseAction, 'Scheduled Geocoding')
426
+ ], ScheduledGeocodingAction);
427
+ export { ScheduledGeocodingAction };
428
+ //# sourceMappingURL=scheduled-geocoding.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled-geocoding.action.js","sourceRoot":"","sources":["../../../src/custom/geo/scheduled-geocoding.action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { ActionResultSimple, RunActionParams } from "@memberjunction/actions-base";
2
+ import { BaseAction } from "@memberjunction/actions";
3
+ /**
4
+ * Action that executes a universal search across the organization's knowledge base.
5
+ *
6
+ * This action delegates to the `@memberjunction/search-engine` singleton which
7
+ * orchestrates vector similarity search, full-text search, entity LIKE-based search,
8
+ * and (optionally) storage file search. Results are fused via Reciprocal Rank Fusion (RRF),
9
+ * deduplicated, and enriched with entity icons, record names, and tags.
10
+ *
11
+ * Designed for use by AI agents (Sage, etc.), MCP connectors, A2A connectors,
12
+ * and workflow orchestration.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Simple search
17
+ * await runAction({
18
+ * ActionName: 'Search',
19
+ * Params: [{ Name: 'Query', Value: 'quarterly revenue by region' }]
20
+ * });
21
+ *
22
+ * // Advanced search with filters
23
+ * await runAction({
24
+ * ActionName: 'Search',
25
+ * Params: [
26
+ * { Name: 'Query', Value: 'customer onboarding process' },
27
+ * { Name: 'MaxResults', Value: 10 },
28
+ * { Name: 'MinScore', Value: 0.3 },
29
+ * { Name: 'EntityNames', Value: 'Documents,Knowledge Articles' },
30
+ * { Name: 'IncludeSources', Value: 'vector,fulltext' }
31
+ * ]
32
+ * });
33
+ * ```
34
+ */
35
+ export declare class SearchAction extends BaseAction {
36
+ protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
37
+ /**
38
+ * Parse the IncludeSources parameter into a SearchSource array.
39
+ * Accepts a comma-separated string.
40
+ */
41
+ private parseIncludeSources;
42
+ /**
43
+ * Parse the EntityNames filter parameter into a string array.
44
+ */
45
+ private parseEntityNames;
46
+ /**
47
+ * Parse the Tags filter parameter into a string array.
48
+ */
49
+ private parseTags;
50
+ /**
51
+ * Build a SearchParams object from the extracted and validated parameters.
52
+ */
53
+ private buildSearchParams;
54
+ /**
55
+ * Build a SearchFilters object from the parsed filter parameters.
56
+ * Returns undefined if no filters are specified.
57
+ */
58
+ private buildFilters;
59
+ /**
60
+ * Convert SearchResultItem objects into serialization-safe formatted results.
61
+ */
62
+ private formatResults;
63
+ private getStringParam;
64
+ private getNumericParam;
65
+ private createErrorResult;
66
+ }
67
+ //# sourceMappingURL=search.action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.action.d.ts","sourceRoot":"","sources":["../../../src/custom/search/search.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAkCrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBACa,YAAa,SAAQ,UAAU;cAExB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiEvF;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,OAAO,CAAC,SAAS;IAMjB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsBzB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;CAO5B"}