@memberjunction/core-actions 5.38.0 โ†’ 5.40.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 (48) hide show
  1. package/dist/custom/ai/run-cluster-analysis.action.d.ts +49 -0
  2. package/dist/custom/ai/run-cluster-analysis.action.d.ts.map +1 -0
  3. package/dist/custom/ai/run-cluster-analysis.action.js +187 -0
  4. package/dist/custom/ai/run-cluster-analysis.action.js.map +1 -0
  5. package/dist/custom/data/search-entity.action.d.ts +48 -0
  6. package/dist/custom/data/search-entity.action.d.ts.map +1 -0
  7. package/dist/custom/data/search-entity.action.js +138 -0
  8. package/dist/custom/data/search-entity.action.js.map +1 -0
  9. package/dist/custom/esignature/base-esignature.action.d.ts +24 -0
  10. package/dist/custom/esignature/base-esignature.action.d.ts.map +1 -0
  11. package/dist/custom/esignature/base-esignature.action.js +68 -0
  12. package/dist/custom/esignature/base-esignature.action.js.map +1 -0
  13. package/dist/custom/esignature/download-signed-document.action.d.ts +15 -0
  14. package/dist/custom/esignature/download-signed-document.action.d.ts.map +1 -0
  15. package/dist/custom/esignature/download-signed-document.action.js +50 -0
  16. package/dist/custom/esignature/download-signed-document.action.js.map +1 -0
  17. package/dist/custom/esignature/get-signature-status.action.d.ts +15 -0
  18. package/dist/custom/esignature/get-signature-status.action.d.ts.map +1 -0
  19. package/dist/custom/esignature/get-signature-status.action.js +48 -0
  20. package/dist/custom/esignature/get-signature-status.action.js.map +1 -0
  21. package/dist/custom/esignature/send-document-for-signature.action.d.ts +24 -0
  22. package/dist/custom/esignature/send-document-for-signature.action.d.ts.map +1 -0
  23. package/dist/custom/esignature/send-document-for-signature.action.js +112 -0
  24. package/dist/custom/esignature/send-document-for-signature.action.js.map +1 -0
  25. package/dist/custom/esignature/void-signature-request.action.d.ts +15 -0
  26. package/dist/custom/esignature/void-signature-request.action.d.ts.map +1 -0
  27. package/dist/custom/esignature/void-signature-request.action.js +47 -0
  28. package/dist/custom/esignature/void-signature-request.action.js.map +1 -0
  29. package/dist/custom/geo/scheduled-geocoding.action.d.ts +27 -0
  30. package/dist/custom/geo/scheduled-geocoding.action.d.ts.map +1 -1
  31. package/dist/custom/geo/scheduled-geocoding.action.js +96 -13
  32. package/dist/custom/geo/scheduled-geocoding.action.js.map +1 -1
  33. package/dist/custom/integration/generate-integration-action.action.d.ts +60 -0
  34. package/dist/custom/integration/generate-integration-action.action.d.ts.map +1 -0
  35. package/dist/custom/integration/generate-integration-action.action.js +182 -0
  36. package/dist/custom/integration/generate-integration-action.action.js.map +1 -0
  37. package/dist/custom/integration/integration-action-executor.d.ts +28 -5
  38. package/dist/custom/integration/integration-action-executor.d.ts.map +1 -1
  39. package/dist/custom/integration/integration-action-executor.js +65 -11
  40. package/dist/custom/integration/integration-action-executor.js.map +1 -1
  41. package/dist/custom/utilities/vectorize-entity.action.d.ts.map +1 -1
  42. package/dist/custom/utilities/vectorize-entity.action.js +7 -2
  43. package/dist/custom/utilities/vectorize-entity.action.js.map +1 -1
  44. package/dist/index.d.ts +8 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +9 -0
  47. package/dist/index.js.map +1 -1
  48. package/package.json +36 -34
@@ -0,0 +1,15 @@
1
+ import { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
2
+ import { BaseSignatureAction } from './base-esignature.action.js';
3
+ /**
4
+ * Action: "Get Signature Status".
5
+ *
6
+ * Polls the provider for the current envelope status of a signature request and persists it.
7
+ * Thin wrapper over {@link SignatureEngine.RefreshStatus}.
8
+ *
9
+ * Inputs: SignatureRequestID (required).
10
+ * Outputs: Status.
11
+ */
12
+ export declare class GetSignatureStatusAction extends BaseSignatureAction {
13
+ protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
14
+ }
15
+ //# sourceMappingURL=get-signature-status.action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-signature-status.action.d.ts","sourceRoot":"","sources":["../../../src/custom/esignature/get-signature-status.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;GAQG;AACH,qBACa,wBAAyB,SAAQ,mBAAmB;cAC7C,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAyB1F"}
@@ -0,0 +1,48 @@
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
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { BaseAction } from '@memberjunction/actions';
9
+ import { BaseSignatureAction } from './base-esignature.action.js';
10
+ /**
11
+ * Action: "Get Signature Status".
12
+ *
13
+ * Polls the provider for the current envelope status of a signature request and persists it.
14
+ * Thin wrapper over {@link SignatureEngine.RefreshStatus}.
15
+ *
16
+ * Inputs: SignatureRequestID (required).
17
+ * Outputs: Status.
18
+ */
19
+ let GetSignatureStatusAction = class GetSignatureStatusAction extends BaseSignatureAction {
20
+ async InternalRunAction(params) {
21
+ const requestId = this.getStringParam(params, 'SignatureRequestID');
22
+ if (!requestId) {
23
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: "Parameter 'SignatureRequestID' is required." };
24
+ }
25
+ try {
26
+ const engine = await this.ensureEngine(params);
27
+ const result = await engine.RefreshStatus(requestId, params.ContextUser);
28
+ if (!result.Success) {
29
+ const notSent = (result.ErrorMessage ?? '').includes('not been sent');
30
+ return {
31
+ Success: false,
32
+ ResultCode: notSent ? 'NOT_SENT' : 'PROVIDER_ERROR',
33
+ Message: result.ErrorMessage ?? 'Status check failed.',
34
+ };
35
+ }
36
+ this.addOutput(params, 'Status', result.status);
37
+ return { Success: true, ResultCode: 'OK', Message: `Status: ${result.status}.`, Params: params.Params };
38
+ }
39
+ catch (e) {
40
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: e instanceof Error ? e.message : String(e) };
41
+ }
42
+ }
43
+ };
44
+ GetSignatureStatusAction = __decorate([
45
+ RegisterClass(BaseAction, 'Get Signature Status')
46
+ ], GetSignatureStatusAction);
47
+ export { GetSignatureStatusAction };
48
+ //# sourceMappingURL=get-signature-status.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-signature-status.action.js","sourceRoot":"","sources":["../../../src/custom/esignature/get-signature-status.action.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;GAQG;AAEI,IAAM,wBAAwB,GAA9B,MAAM,wBAAyB,SAAQ,mBAAmB;IACnD,KAAK,CAAC,iBAAiB,CAAC,MAAuB;QACrD,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;QACpE,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC;QACpH,CAAC;QAED,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAEzE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;gBACtE,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB;oBACnD,OAAO,EAAE,MAAM,CAAC,YAAY,IAAI,sBAAsB;iBACzD,CAAC;YACN,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,MAAM,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC5G,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjH,CAAC;IACL,CAAC;CACJ,CAAA;AA1BY,wBAAwB;IADpC,aAAa,CAAC,UAAU,EAAE,sBAAsB,CAAC;GACrC,wBAAwB,CA0BpC"}
@@ -0,0 +1,24 @@
1
+ import { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
2
+ import { BaseSignatureAction } from './base-esignature.action.js';
3
+ /**
4
+ * Action: "Send Document for Signature".
5
+ *
6
+ * Sends one or more documents through a configured signature account/provider and records the
7
+ * envelope lifecycle. Thin wrapper over {@link SignatureEngine.SendForSignature}.
8
+ *
9
+ * Inputs:
10
+ * - SignatureAccountID (required)
11
+ * - Title (required)
12
+ * - Documents (required) โ€” array of `{ filename, contentType?, contentBase64 }`
13
+ * - Recipients (required) โ€” array of `{ email, name?, routingOrder?, role? }`
14
+ * - Message?, EntityID?, RecordID?, ArtifactID?, ArtifactVersionID?, SendImmediately?, Metadata?
15
+ *
16
+ * Outputs: SignatureRequestID, ExternalEnvelopeID, Status.
17
+ */
18
+ export declare class SendDocumentForSignatureAction extends BaseSignatureAction {
19
+ protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
20
+ private parseDocuments;
21
+ private parseRecipients;
22
+ private fail;
23
+ }
24
+ //# sourceMappingURL=send-document-for-signature.action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-document-for-signature.action.d.ts","sourceRoot":"","sources":["../../../src/custom/esignature/send-document-for-signature.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAInF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAU/D;;;;;;;;;;;;;;GAcG;AACH,qBACa,8BAA+B,SAAQ,mBAAmB;cACnD,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA+DvF,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,IAAI;CAMf"}
@@ -0,0 +1,112 @@
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
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { BaseAction } from '@memberjunction/actions';
9
+ import { BaseSignatureAction } from './base-esignature.action.js';
10
+ /**
11
+ * Action: "Send Document for Signature".
12
+ *
13
+ * Sends one or more documents through a configured signature account/provider and records the
14
+ * envelope lifecycle. Thin wrapper over {@link SignatureEngine.SendForSignature}.
15
+ *
16
+ * Inputs:
17
+ * - SignatureAccountID (required)
18
+ * - Title (required)
19
+ * - Documents (required) โ€” array of `{ filename, contentType?, contentBase64 }`
20
+ * - Recipients (required) โ€” array of `{ email, name?, routingOrder?, role? }`
21
+ * - Message?, EntityID?, RecordID?, ArtifactID?, ArtifactVersionID?, SendImmediately?, Metadata?
22
+ *
23
+ * Outputs: SignatureRequestID, ExternalEnvelopeID, Status.
24
+ */
25
+ let SendDocumentForSignatureAction = class SendDocumentForSignatureAction extends BaseSignatureAction {
26
+ async InternalRunAction(params) {
27
+ const signatureAccountId = this.getStringParam(params, 'SignatureAccountID');
28
+ const title = this.getStringParam(params, 'Title');
29
+ const documents = this.parseDocuments(params);
30
+ const recipients = this.parseRecipients(params);
31
+ const artifactId = this.getStringParam(params, 'ArtifactID');
32
+ const artifactVersionId = this.getStringParam(params, 'ArtifactVersionID');
33
+ if (!signatureAccountId) {
34
+ return this.fail('ACCOUNT_NOT_FOUND', "Parameter 'SignatureAccountID' is required.");
35
+ }
36
+ if (!title) {
37
+ return this.fail('PROVIDER_ERROR', "Parameter 'Title' is required.");
38
+ }
39
+ // Documents may be supplied inline (base64) OR by reference to an Artifact Version already
40
+ // in MJ. Require at least one source.
41
+ if (!documents.length && !artifactVersionId) {
42
+ return this.fail('MISSING_DOCUMENT', "Provide 'Documents' (each with contentBase64) or an 'ArtifactVersionID' to send a document already in MemberJunction.");
43
+ }
44
+ if (!recipients.length) {
45
+ return this.fail('PROVIDER_ERROR', "Parameter 'Recipients' must contain at least one recipient with an email.");
46
+ }
47
+ try {
48
+ const engine = await this.ensureEngine(params);
49
+ const result = await engine.SendForSignature({
50
+ signatureAccountId,
51
+ title,
52
+ message: this.getStringParam(params, 'Message'),
53
+ documents,
54
+ recipients,
55
+ artifactId,
56
+ artifactVersionId,
57
+ entityId: this.getStringParam(params, 'EntityID'),
58
+ recordId: this.getStringParam(params, 'RecordID'),
59
+ sendImmediately: this.getBooleanParam(params, 'SendImmediately', true),
60
+ metadata: this.getObjectParam(params, 'Metadata'),
61
+ contextUser: params.ContextUser,
62
+ });
63
+ if (!result.Success) {
64
+ return this.fail('PROVIDER_ERROR', result.ErrorMessage ?? 'Send failed.', params, result.signatureRequestId);
65
+ }
66
+ this.addOutput(params, 'SignatureRequestID', result.signatureRequestId);
67
+ this.addOutput(params, 'ExternalEnvelopeID', result.externalEnvelopeId);
68
+ this.addOutput(params, 'Status', result.status);
69
+ const sentImmediately = this.getBooleanParam(params, 'SendImmediately', true);
70
+ return {
71
+ Success: true,
72
+ ResultCode: sentImmediately ? 'SENT' : 'DRAFT_CREATED',
73
+ Message: `Signature request ${result.signatureRequestId} (${result.status}).`,
74
+ Params: params.Params,
75
+ };
76
+ }
77
+ catch (e) {
78
+ return this.fail('PROVIDER_ERROR', e instanceof Error ? e.message : String(e), params);
79
+ }
80
+ }
81
+ parseDocuments(params) {
82
+ const raw = this.getObjectParam(params, 'Documents');
83
+ if (!Array.isArray(raw)) {
84
+ return [];
85
+ }
86
+ return raw
87
+ .filter((d) => d && d.filename && d.contentBase64)
88
+ .map((d) => ({
89
+ filename: d.filename,
90
+ contentType: d.contentType ?? 'application/pdf',
91
+ bytes: Buffer.from(d.contentBase64, 'base64'),
92
+ }));
93
+ }
94
+ parseRecipients(params) {
95
+ const raw = this.getObjectParam(params, 'Recipients');
96
+ if (!Array.isArray(raw)) {
97
+ return [];
98
+ }
99
+ return raw.filter((r) => r && r.email);
100
+ }
101
+ fail(resultCode, message, params, signatureRequestId) {
102
+ if (params && signatureRequestId) {
103
+ this.addOutput(params, 'SignatureRequestID', signatureRequestId);
104
+ }
105
+ return { Success: false, ResultCode: resultCode, Message: message, Params: params?.Params };
106
+ }
107
+ };
108
+ SendDocumentForSignatureAction = __decorate([
109
+ RegisterClass(BaseAction, 'Send Document for Signature')
110
+ ], SendDocumentForSignatureAction);
111
+ export { SendDocumentForSignatureAction };
112
+ //# sourceMappingURL=send-document-for-signature.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-document-for-signature.action.js","sourceRoot":"","sources":["../../../src/custom/esignature/send-document-for-signature.action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
2
+ import { BaseSignatureAction } from './base-esignature.action.js';
3
+ /**
4
+ * Action: "Void Signature Request".
5
+ *
6
+ * Voids/cancels an in-flight signature request with a reason. Thin wrapper over
7
+ * {@link SignatureEngine.Void}.
8
+ *
9
+ * Inputs: SignatureRequestID (required), Reason (required).
10
+ * Outputs: Status.
11
+ */
12
+ export declare class VoidSignatureRequestAction extends BaseSignatureAction {
13
+ protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
14
+ }
15
+ //# sourceMappingURL=void-signature-request.action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"void-signature-request.action.d.ts","sourceRoot":"","sources":["../../../src/custom/esignature/void-signature-request.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;GAQG;AACH,qBACa,0BAA2B,SAAQ,mBAAmB;cAC/C,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAwB1F"}
@@ -0,0 +1,47 @@
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
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { BaseAction } from '@memberjunction/actions';
9
+ import { BaseSignatureAction } from './base-esignature.action.js';
10
+ /**
11
+ * Action: "Void Signature Request".
12
+ *
13
+ * Voids/cancels an in-flight signature request with a reason. Thin wrapper over
14
+ * {@link SignatureEngine.Void}.
15
+ *
16
+ * Inputs: SignatureRequestID (required), Reason (required).
17
+ * Outputs: Status.
18
+ */
19
+ let VoidSignatureRequestAction = class VoidSignatureRequestAction extends BaseSignatureAction {
20
+ async InternalRunAction(params) {
21
+ const requestId = this.getStringParam(params, 'SignatureRequestID');
22
+ const reason = this.getStringParam(params, 'Reason');
23
+ if (!requestId) {
24
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: "Parameter 'SignatureRequestID' is required." };
25
+ }
26
+ if (!reason) {
27
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: "Parameter 'Reason' is required." };
28
+ }
29
+ try {
30
+ const engine = await this.ensureEngine(params);
31
+ const result = await engine.Void(requestId, reason, params.ContextUser);
32
+ if (!result.Success) {
33
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: result.ErrorMessage ?? 'Void failed.' };
34
+ }
35
+ this.addOutput(params, 'Status', 'Voided');
36
+ return { Success: true, ResultCode: 'VOIDED', Message: 'Signature request voided.', Params: params.Params };
37
+ }
38
+ catch (e) {
39
+ return { Success: false, ResultCode: 'PROVIDER_ERROR', Message: e instanceof Error ? e.message : String(e) };
40
+ }
41
+ }
42
+ };
43
+ VoidSignatureRequestAction = __decorate([
44
+ RegisterClass(BaseAction, 'Void Signature Request')
45
+ ], VoidSignatureRequestAction);
46
+ export { VoidSignatureRequestAction };
47
+ //# sourceMappingURL=void-signature-request.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"void-signature-request.action.js","sourceRoot":"","sources":["../../../src/custom/esignature/void-signature-request.action.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;GAQG;AAEI,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,mBAAmB;IACrD,KAAK,CAAC,iBAAiB,CAAC,MAAuB;QACrD,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC;QACpH,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;QACxG,CAAC;QAED,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAExE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,YAAY,IAAI,cAAc,EAAE,CAAC;YAC5G,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAChH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjH,CAAC;IACL,CAAC;CACJ,CAAA;AAzBY,0BAA0B;IADtC,aAAa,CAAC,UAAU,EAAE,wBAAwB,CAAC;GACvC,0BAA0B,CAyBtC"}
@@ -44,6 +44,8 @@ export declare class ScheduledGeocodingAction extends BaseAction {
44
44
  private static readonly DEFAULT_MAX_TOTAL;
45
45
  /** Page size for RunView pagination โ€” controls how many BaseEntity objects exist simultaneously. */
46
46
  private static readonly PAGE_SIZE;
47
+ /** Indentation prefix for child log lines nested under a group/entity header. */
48
+ private static readonly INDENT;
47
49
  /**
48
50
  * Geocoding provider name in effect for the current run. Set at the top of
49
51
  * InternalRunAction from the 'GeocodingProvider' action parameter and read
@@ -128,6 +130,9 @@ export declare class ScheduledGeocodingAction extends BaseAction {
128
130
  * @param contextUser - User context
129
131
  * @param batchSize - Number of concurrent geocoding operations per batch
130
132
  * @param existingGeoCodesMap - Optional pre-loaded map passed to GeoCodeSyncService
133
+ * @param progress - Optional console progress context. When supplied, each batch logs a
134
+ * cumulative `records Xโ€“Y of Total` line (anchored to `progress.Offset`) instead of a
135
+ * per-call `batch N/M` line that resets every page.
131
136
  */
132
137
  private geocodeBatch;
133
138
  /**
@@ -155,6 +160,28 @@ export declare class ScheduledGeocodingAction extends BaseAction {
155
160
  * exactly the same contract as a real save event.
156
161
  */
157
162
  private fireSyntheticSaveEvent;
163
+ /**
164
+ * Format an integer with locale thousands separators for console output
165
+ * (e.g. `2000` โ†’ `2,000`). Pinned to `en-US` for deterministic logs.
166
+ */
167
+ private fmt;
168
+ /**
169
+ * Emit the group header that precedes a single entity's batch lines, e.g.
170
+ * `๐Ÿ“ Members โ€” 2,000 records with address data`.
171
+ */
172
+ private logEntityHeader;
173
+ /**
174
+ * Emit one indented, cumulative batch line beneath the entity header, e.g.
175
+ * ` โœ“ records 1,991โ€“2,000 of 2,000 ยท 10/10 (page 4)`. The range is anchored
176
+ * to `progress.Offset` so the numbers run continuously across every page.
177
+ */
178
+ private logBatchProgress;
179
+ /**
180
+ * Emit the indented per-entity completion line, reconciling geocoded vs.
181
+ * failed vs. already-current against the candidate total, e.g.
182
+ * ` โœ… Members โ€” 1,974 geocoded, 26 already current`.
183
+ */
184
+ private logEntityComplete;
158
185
  /**
159
186
  * Get the geo address fields (non-lat/lng) for an entity.
160
187
  * These are the fields that, when non-null, indicate a record
@@ -1 +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;AAWrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBACa,wBAAyB,SAAQ,UAAU;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAChD,qGAAqG;IACrG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IACnD,oGAAoG;IACpG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAO;IAExC;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB,CAAuB;cAEvC,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgDvF;;;;OAIG;YACW,qBAAqB;IA2BnC;;;;;;;;;;OAUG;YACW,uBAAuB;IA+ErC;;;;;;;;;OASG;YACW,oBAAoB;IAwBlC;;;OAGG;YACW,oBAAoB;IA2BlC;;;;OAIG;YACW,oBAAoB;IAkElC;;;OAGG;YACW,kBAAkB;IA6BhC;;;;OAIG;YACW,sBAAsB;IAgDpC;;;;OAIG;YACW,uBAAuB;IAmDrC;;;;;;;;;;;;;;;OAeG;YACW,YAAY;IA2B1B;;;;;;;;;;OAUG;YACW,oBAAoB;IA2BlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAsB9B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;;;;;;;;OASG;YACW,uBAAuB;IAoCrC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;CAMzB"}
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;AA2BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBACa,wBAAyB,SAAQ,UAAU;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAChD,qGAAqG;IACrG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IACnD,oGAAoG;IACpG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAO;IACxC,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEvC;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB,CAAuB;cAEvC,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgDvF;;;;OAIG;YACW,qBAAqB;IA2BnC;;;;;;;;;;OAUG;YACW,uBAAuB;IAoGrC;;;;;;;;;OASG;YACW,oBAAoB;IAyBlC;;;OAGG;YACW,oBAAoB;IA4BlC;;;;OAIG;YACW,oBAAoB;IAwElC;;;OAGG;YACW,kBAAkB;IA6BhC;;;;OAIG;YACW,sBAAsB;IAgDpC;;;;OAIG;YACW,uBAAuB;IAmDrC;;;;;;;;;;;;;;;;;;OAkBG;YACW,YAAY;IA4B1B;;;;;;;;;;OAUG;YACW,oBAAoB;IA2BlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAsB9B;;;OAGG;IACH,OAAO,CAAC,GAAG;IAIX;;;OAGG;IACH,OAAO,CAAC,eAAe;IAOvB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;;;;;;;;OASG;YACW,uBAAuB;IAoCrC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;CAMzB"}
@@ -69,6 +69,8 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
69
69
  static { this.DEFAULT_MAX_TOTAL = 50_000; }
70
70
  /** Page size for RunView pagination โ€” controls how many BaseEntity objects exist simultaneously. */
71
71
  static { this.PAGE_SIZE = 500; }
72
+ /** Indentation prefix for child log lines nested under a group/entity header. */
73
+ static { this.INDENT = ' '; }
72
74
  async InternalRunAction(params) {
73
75
  const contextUser = params.ContextUser;
74
76
  if (!contextUser) {
@@ -79,7 +81,7 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
79
81
  const maxTotal = this.getNumericParam(params, 'MaxTotalRecords', ScheduledGeocodingAction_1.DEFAULT_MAX_TOTAL);
80
82
  const geocodingProvider = this.getStringParam(params, 'GeocodingProvider');
81
83
  this.currentGeocodingProvider = geocodingProvider ?? null;
82
- LogStatus(`ScheduledGeocodingAction: Starting maintenance run (BatchSize=${batchSize}, MaxTotal=${maxTotal}, GeocodingProvider=${geocodingProvider ?? 'config-default'})`);
84
+ LogStatus(`๐ŸŒ Scheduled Geocoding โ€” starting (batch ${batchSize} ยท max ${this.fmt(maxTotal)} ยท provider ${geocodingProvider ?? 'config-default'})`);
83
85
  const stats = { MissingProcessed: 0, MissingSuccess: 0, RetriesProcessed: 0, RetriesSuccess: 0, OrphansRemoved: 0 };
84
86
  // Step 1: Find and geocode missing records
85
87
  const missingStats = await this.processMissingRecords(contextUser, batchSize, maxTotal, params.Provider);
@@ -94,9 +96,9 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
94
96
  stats.OrphansRemoved = await this.cleanupOrphanedRecords(contextUser, batchSize, params.Provider);
95
97
  const totalRecordsProcessed = stats.MissingProcessed + stats.RetriesProcessed;
96
98
  if (totalRecordsProcessed >= maxTotal) {
97
- LogStatus(`ScheduledGeocodingAction: WARNING โ€” MaxTotal limit (${maxTotal}) reached. There may be remaining records for the next run.`);
99
+ LogStatus(`โš ๏ธ MaxTotal limit (${this.fmt(maxTotal)}) reached โ€” remaining records will be picked up on the next run.`);
98
100
  }
99
- LogStatus(`ScheduledGeocodingAction: Complete โ€” Missing: ${stats.MissingProcessed} processed (${stats.MissingSuccess} success), Retries: ${stats.RetriesProcessed} (${stats.RetriesSuccess} success), Orphans: ${stats.OrphansRemoved} removed`);
101
+ LogStatus(`๐Ÿ Scheduled Geocoding โ€” complete ยท missing ${this.fmt(stats.MissingProcessed)} (${this.fmt(stats.MissingSuccess)} โœ“) ยท retries ${this.fmt(stats.RetriesProcessed)} (${this.fmt(stats.RetriesSuccess)} โœ“) ยท orphans ${this.fmt(stats.OrphansRemoved)} removed`);
100
102
  return {
101
103
  Success: true,
102
104
  ResultCode: 'SUCCESS',
@@ -125,7 +127,7 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
125
127
  totalSuccess += entityStats.Success;
126
128
  }
127
129
  if (totalProcessed > 0) {
128
- LogStatus(`ScheduledGeocodingAction: Processed ${totalProcessed} missing records (${totalSuccess} geocoded successfully)`);
130
+ LogStatus(`โœ… Missing-record geocoding complete โ€” ${this.fmt(totalProcessed)} processed, ${this.fmt(totalSuccess)} geocoded`);
129
131
  }
130
132
  return { Processed: totalProcessed, Success: totalSuccess };
131
133
  }
@@ -160,15 +162,23 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
160
162
  let totalSuccess = 0;
161
163
  let pageOffset = 0; // OFFSET-mode (composite-PK fallback)
162
164
  let lastSeenKey; // keyset mode
165
+ let pageNumber = 0; // 1-based, shown in batch lines
166
+ let candidateTotal = 0; // denominator: records with address data (from page-1 TotalRowCount)
167
+ let headerLogged = false; // emit the entity header lazily, only when there's work to do
163
168
  // Paginate through entity records to avoid loading all into memory at once.
164
169
  // Keyset pagination keeps each page O(log N) regardless of depth โ€” a critical
165
170
  // win when the entity has millions of rows. See guides/KEYSET_PAGINATION_GUIDE.md.
166
171
  while (totalProcessed < maxRows) {
172
+ pageNumber++;
167
173
  const pageResult = canUseKeyset
168
174
  ? await this.loadEntityPageKeyset(entityInfo.Name, pkField.Name, nonNullConditions, lastSeenKey, contextUser)
169
175
  : await this.loadEntityPageOffset(entityInfo.Name, nonNullConditions, pageOffset, contextUser);
170
176
  if (!pageResult.Success || pageResult.Results.length === 0)
171
177
  break;
178
+ // TotalRowCount reports the full count matching the filter regardless of the
179
+ // keyset seek predicate / page offset, so the first page gives us a stable denominator.
180
+ if (candidateTotal === 0)
181
+ candidateTotal = pageResult.TotalRowCount;
172
182
  const pageEntities = pageResult.Results;
173
183
  // Filter to records missing geocodes (not in existingMap for any LocationType)
174
184
  const missingRecords = pageEntities.filter(entity => {
@@ -179,9 +189,18 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
179
189
  return !existingMap.has(key);
180
190
  });
181
191
  if (missingRecords.length > 0) {
192
+ if (!headerLogged) {
193
+ this.logEntityHeader(entityInfo, candidateTotal);
194
+ headerLogged = true;
195
+ }
182
196
  const budget = maxRows - totalProcessed;
183
197
  const toProcess = missingRecords.slice(0, budget);
184
- const stats = await this.geocodeBatch(toProcess, entityInfo, contextUser, batchSize, existingMap);
198
+ const stats = await this.geocodeBatch(toProcess, entityInfo, contextUser, batchSize, existingMap, {
199
+ Label: entityInfo.Name,
200
+ Total: candidateTotal,
201
+ Offset: totalProcessed,
202
+ Page: pageNumber
203
+ });
185
204
  totalProcessed += stats.Processed;
186
205
  totalSuccess += stats.Success;
187
206
  }
@@ -200,6 +219,9 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
200
219
  pageOffset += ScheduledGeocodingAction_1.PAGE_SIZE;
201
220
  }
202
221
  }
222
+ if (headerLogged) {
223
+ this.logEntityComplete(entityInfo, totalProcessed, totalSuccess, candidateTotal);
224
+ }
203
225
  return { Processed: totalProcessed, Success: totalSuccess };
204
226
  }
205
227
  catch (e) {
@@ -231,7 +253,8 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
231
253
  }, contextUser);
232
254
  return {
233
255
  Success: result.Success,
234
- Results: result.Success ? result.Results : []
256
+ Results: result.Success ? result.Results : [],
257
+ TotalRowCount: result.Success ? result.TotalRowCount : 0
235
258
  };
236
259
  }
237
260
  /**
@@ -251,7 +274,8 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
251
274
  }, contextUser);
252
275
  return {
253
276
  Success: result.Success,
254
- Results: result.Success ? result.Results : []
277
+ Results: result.Success ? result.Results : [],
278
+ TotalRowCount: result.Success ? result.TotalRowCount : 0
255
279
  };
256
280
  }
257
281
  // ================================================================
@@ -270,6 +294,7 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
270
294
  let totalProcessed = 0;
271
295
  let totalSuccess = 0;
272
296
  let pageOffset = 0;
297
+ let retryTotal = 0; // denominator: total failed-and-retriable rows (from page-1 TotalRowCount)
273
298
  while (totalProcessed < maxTotal) {
274
299
  const pageResult = await rv.RunView({
275
300
  EntityName: 'MJ: Record Geo Codes',
@@ -285,7 +310,8 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
285
310
  const budget = maxTotal - totalProcessed;
286
311
  const records = pageResult.Results.slice(0, budget);
287
312
  if (totalProcessed === 0) {
288
- LogStatus(`ScheduledGeocodingAction: Processing failed records for retry`);
313
+ retryTotal = Math.min(pageResult.TotalRowCount, maxTotal);
314
+ LogStatus(`๐Ÿ” Retrying failed geocodes โ€” ${this.fmt(retryTotal)} pending`);
289
315
  }
290
316
  // Group by entity for efficient processing
291
317
  const byEntity = new Map();
@@ -305,7 +331,11 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
305
331
  const entities = await this.loadSourceEntities(geoRecords, entityInfo, contextUser, provider);
306
332
  if (entities.length === 0)
307
333
  continue;
308
- const stats = await this.geocodeBatch(entities, entityInfo, contextUser, batchSize);
334
+ const stats = await this.geocodeBatch(entities, entityInfo, contextUser, batchSize, undefined, {
335
+ Label: entityInfo.Name,
336
+ Total: retryTotal,
337
+ Offset: totalProcessed
338
+ });
309
339
  totalProcessed += stats.Processed;
310
340
  totalSuccess += stats.Success;
311
341
  }
@@ -418,7 +448,7 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
418
448
  hasMore = orphans.length >= ScheduledGeocodingAction_1.PAGE_SIZE;
419
449
  }
420
450
  if (entityRemoved > 0) {
421
- LogStatus(`ScheduledGeocodingAction: Removed ${entityRemoved} orphaned geo records for ${entityInfo.Name}`);
451
+ LogStatus(`๐Ÿงน ${entityInfo.Name} โ€” removed ${this.fmt(entityRemoved)} orphaned geo records`);
422
452
  }
423
453
  return entityRemoved;
424
454
  }
@@ -440,16 +470,19 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
440
470
  * @param contextUser - User context
441
471
  * @param batchSize - Number of concurrent geocoding operations per batch
442
472
  * @param existingGeoCodesMap - Optional pre-loaded map passed to GeoCodeSyncService
473
+ * @param progress - Optional console progress context. When supplied, each batch logs a
474
+ * cumulative `records Xโ€“Y of Total` line (anchored to `progress.Offset`) instead of a
475
+ * per-call `batch N/M` line that resets every page.
443
476
  */
444
- async geocodeBatch(entities, entityInfo, contextUser, batchSize, existingGeoCodesMap) {
477
+ async geocodeBatch(entities, entityInfo, contextUser, batchSize, existingGeoCodesMap, progress) {
445
478
  let totalSuccess = 0;
446
479
  for (let i = 0; i < entities.length; i += batchSize) {
447
480
  const batch = entities.slice(i, i + batchSize);
448
481
  const results = await Promise.allSettled(batch.map(entity => this.geocodeAndInvalidate(entity, contextUser, existingGeoCodesMap)));
449
482
  const batchSuccess = results.filter(r => r.status === 'fulfilled' && r.value).length;
450
483
  totalSuccess += batchSuccess;
451
- if (entities.length > batchSize) {
452
- LogStatus(`ScheduledGeocodingAction: ${entityInfo.Name} batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(entities.length / batchSize)} โ€” ${batchSuccess}/${batch.length} success`);
484
+ if (progress) {
485
+ this.logBatchProgress(progress, i, batch.length, batchSuccess);
453
486
  }
454
487
  }
455
488
  return { Processed: entities.length, Success: totalSuccess };
@@ -513,6 +546,56 @@ let ScheduledGeocodingAction = class ScheduledGeocodingAction extends BaseAction
513
546
  });
514
547
  }
515
548
  // ================================================================
549
+ // Console UX helpers
550
+ // ================================================================
551
+ /**
552
+ * Format an integer with locale thousands separators for console output
553
+ * (e.g. `2000` โ†’ `2,000`). Pinned to `en-US` for deterministic logs.
554
+ */
555
+ fmt(n) {
556
+ return n.toLocaleString('en-US');
557
+ }
558
+ /**
559
+ * Emit the group header that precedes a single entity's batch lines, e.g.
560
+ * `๐Ÿ“ Members โ€” 2,000 records with address data`.
561
+ */
562
+ logEntityHeader(entityInfo, candidateTotal) {
563
+ const scope = candidateTotal > 0
564
+ ? `${this.fmt(candidateTotal)} records with address data`
565
+ : 'records with address data';
566
+ LogStatus(`๐Ÿ“ ${entityInfo.Name} โ€” ${scope}`);
567
+ }
568
+ /**
569
+ * Emit one indented, cumulative batch line beneath the entity header, e.g.
570
+ * ` โœ“ records 1,991โ€“2,000 of 2,000 ยท 10/10 (page 4)`. The range is anchored
571
+ * to `progress.Offset` so the numbers run continuously across every page.
572
+ */
573
+ logBatchProgress(progress, localIndex, batchCount, batchSuccess) {
574
+ const start = progress.Offset + localIndex + 1;
575
+ const end = progress.Offset + localIndex + batchCount;
576
+ const total = progress.Total > 0 ? this.fmt(progress.Total) : '?';
577
+ const allOk = batchSuccess === batchCount;
578
+ const icon = allOk ? 'โœ“' : 'โš ';
579
+ const failNote = allOk ? '' : ` ยท ${this.fmt(batchCount - batchSuccess)} failed`;
580
+ const pageNote = progress.Page != null ? ` (page ${progress.Page})` : '';
581
+ LogStatus(`${ScheduledGeocodingAction_1.INDENT}${icon} records ${this.fmt(start)}โ€“${this.fmt(end)} of ${total} ยท ${batchSuccess}/${batchCount}${failNote}${pageNote}`);
582
+ }
583
+ /**
584
+ * Emit the indented per-entity completion line, reconciling geocoded vs.
585
+ * failed vs. already-current against the candidate total, e.g.
586
+ * ` โœ… Members โ€” 1,974 geocoded, 26 already current`.
587
+ */
588
+ logEntityComplete(entityInfo, processed, success, candidateTotal) {
589
+ const failed = processed - success;
590
+ const alreadyCurrent = candidateTotal > 0 ? Math.max(0, candidateTotal - processed) : 0;
591
+ const parts = [`${this.fmt(success)} geocoded`];
592
+ if (failed > 0)
593
+ parts.push(`${this.fmt(failed)} failed`);
594
+ if (alreadyCurrent > 0)
595
+ parts.push(`${this.fmt(alreadyCurrent)} already current`);
596
+ LogStatus(`${ScheduledGeocodingAction_1.INDENT}โœ… ${entityInfo.Name} โ€” ${parts.join(', ')}`);
597
+ }
598
+ // ================================================================
516
599
  // Helpers
517
600
  // ================================================================
518
601
  /**