Skip to content

Commit bc0e4b3

Browse files
authored
Initial Commit (#349)
1 parent d5c9125 commit bc0e4b3

7 files changed

Lines changed: 186 additions & 141 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@salesforce/mrt-utilities': patch
3+
---
4+
5+
Fixed CommonJS packaging for Node 22 by ensuring `require` entrypoints under `dist/cjs` are emitted as true CJS modules. Added dedicated `@salesforce/mrt-utilities/data-store` and `@salesforce/mrt-utilities/middleware/express` entrypoints so data-store imports do not have to load the Express middleware barrel during startup.

packages/mrt-utilities/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@
4040
"default": "./dist/cjs/middleware/index.js"
4141
}
4242
},
43+
"./middleware/express": {
44+
"development": "./src/middleware/express.ts",
45+
"import": {
46+
"types": "./dist/esm/middleware/express.d.ts",
47+
"default": "./dist/esm/middleware/express.js"
48+
},
49+
"require": {
50+
"types": "./dist/cjs/middleware/express.d.ts",
51+
"default": "./dist/cjs/middleware/express.js"
52+
}
53+
},
54+
"./data-store": {
55+
"development": "./src/data-store/index.ts",
56+
"import": {
57+
"types": "./dist/esm/data-store/index.d.ts",
58+
"default": "./dist/esm/data-store/index.js"
59+
},
60+
"require": {
61+
"types": "./dist/cjs/data-store/index.d.ts",
62+
"default": "./dist/cjs/data-store/index.js"
63+
}
64+
},
4365
"./metrics": {
4466
"development": "./src/metrics/index.ts",
4567
"import": {
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* SPDX-License-Identifier: Apache-2
4+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
8+
import {DynamoDBDocumentClient, GetCommand, type GetCommandOutput} from '@aws-sdk/lib-dynamodb';
9+
10+
import {logMRTError} from '../utils/utils.js';
11+
12+
export class DataStoreNotFoundError extends Error {
13+
constructor(message: string) {
14+
super(message);
15+
this.name = 'DataStoreNotFoundError';
16+
Object.setPrototypeOf(this, DataStoreNotFoundError.prototype);
17+
}
18+
}
19+
20+
export class DataStoreServiceError extends Error {
21+
constructor(message: string) {
22+
super(message);
23+
this.name = 'DataStoreServiceError';
24+
Object.setPrototypeOf(this, DataStoreServiceError.prototype);
25+
}
26+
}
27+
28+
export class DataStoreUnavailableError extends Error {
29+
constructor(message: string) {
30+
super(message);
31+
this.name = 'DataStoreUnavailableError';
32+
Object.setPrototypeOf(this, DataStoreUnavailableError.prototype);
33+
}
34+
}
35+
36+
/**
37+
* A class for reading entries from the data store.
38+
*
39+
* This class uses a singleton pattern.
40+
* Use DataStore.getDataStore() to get the singleton instance.
41+
*/
42+
export class DataStore {
43+
private _tableName: string = '';
44+
private _ddb: DynamoDBDocumentClient | null = null;
45+
private static _instance: DataStore | null = null;
46+
47+
/** @internal Test hook: inject a document client for unit tests */
48+
static _testDocumentClient: DynamoDBDocumentClient | null = null;
49+
/** @internal Test hook: inject logMRTError for unit tests */
50+
static _testLogMRTError: ((namespace: string, err: unknown, context?: Record<string, unknown>) => void) | null = null;
51+
52+
private constructor() {
53+
// Private constructor for singleton; use DataStore.getDataStore() instead.
54+
}
55+
56+
/**
57+
* Get or create a DynamoDB document client (for abstraction of attribute values).
58+
*
59+
* @private
60+
* @returns The DynamoDB document client
61+
* @throws {DataStoreUnavailableError} The data store is unavailable
62+
*/
63+
private getClient(): DynamoDBDocumentClient {
64+
if (!this.isDataStoreAvailable()) {
65+
throw new DataStoreUnavailableError('The data store is unavailable.');
66+
}
67+
68+
if (DataStore._testDocumentClient) {
69+
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
70+
return DataStore._testDocumentClient;
71+
}
72+
73+
if (!this._ddb) {
74+
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
75+
this._ddb = DynamoDBDocumentClient.from(
76+
new DynamoDBClient({
77+
region: process.env.AWS_REGION,
78+
}),
79+
);
80+
}
81+
82+
return this._ddb;
83+
}
84+
85+
/**
86+
* Get or create the singleton DataStore instance.
87+
*
88+
* @returns The singleton DataStore instance
89+
*/
90+
static getDataStore(): DataStore {
91+
if (!DataStore._instance) {
92+
DataStore._instance = new DataStore();
93+
}
94+
return DataStore._instance;
95+
}
96+
97+
/**
98+
* Whether the data store can be used in the current environment.
99+
*
100+
* @returns true if the data store is available, false otherwise
101+
*/
102+
isDataStoreAvailable(): boolean {
103+
return Boolean(process.env.AWS_REGION && process.env.MOBIFY_PROPERTY_ID && process.env.DEPLOY_TARGET);
104+
}
105+
106+
/**
107+
* Fetch an entry from the data store.
108+
*
109+
* @param key The data store entry's key
110+
* @returns An object containing the entry's key and value
111+
* @throws {DataStoreUnavailableError} The data store is unavailable
112+
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
113+
* @throws {DataStoreServiceError} An internal error occurred
114+
*/
115+
async getEntry(key: string): Promise<Record<string, unknown> | undefined> {
116+
if (!this.isDataStoreAvailable()) {
117+
throw new DataStoreUnavailableError('The data store is unavailable.');
118+
}
119+
120+
const ddb = this.getClient();
121+
let response: GetCommandOutput;
122+
try {
123+
response = await ddb.send(
124+
new GetCommand({
125+
TableName: this._tableName,
126+
Key: {
127+
projectEnvironment: `${process.env.MOBIFY_PROPERTY_ID} ${process.env.DEPLOY_TARGET}`,
128+
key,
129+
},
130+
}),
131+
);
132+
} catch (error) {
133+
const logFn = DataStore._testLogMRTError ?? logMRTError;
134+
logFn('data_store', error, {key, tableName: this._tableName});
135+
throw new DataStoreServiceError('Data store request failed.');
136+
}
137+
138+
if (!response.Item?.value) {
139+
throw new DataStoreNotFoundError(`Data store entry '${key}' not found.`);
140+
}
141+
142+
return {key, value: response.Item.value};
143+
}
144+
}

packages/mrt-utilities/src/middleware/data-store.ts

Lines changed: 3 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,141 +4,8 @@
44
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
55
*/
66

7-
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
8-
import {DynamoDBDocumentClient, GetCommand, type GetCommandOutput} from '@aws-sdk/lib-dynamodb';
9-
10-
import {logMRTError} from '../utils/utils.js';
11-
12-
export class DataStoreNotFoundError extends Error {
13-
constructor(message: string) {
14-
super(message);
15-
this.name = 'DataStoreNotFoundError';
16-
Object.setPrototypeOf(this, DataStoreNotFoundError.prototype);
17-
}
18-
}
19-
20-
export class DataStoreServiceError extends Error {
21-
constructor(message: string) {
22-
super(message);
23-
this.name = 'DataStoreServiceError';
24-
Object.setPrototypeOf(this, DataStoreServiceError.prototype);
25-
}
26-
}
27-
28-
export class DataStoreUnavailableError extends Error {
29-
constructor(message: string) {
30-
super(message);
31-
this.name = 'DataStoreUnavailableError';
32-
Object.setPrototypeOf(this, DataStoreUnavailableError.prototype);
33-
}
34-
}
35-
367
/**
37-
* A class for reading entries from the data store.
38-
*
39-
* This class uses a singleton pattern.
40-
* Use DataStore.getDataStore() to get the singleton instance.
8+
* @deprecated Import data-store symbols from `@salesforce/mrt-utilities/data-store`.
9+
* This compatibility path is kept for backward compatibility.
4110
*/
42-
export class DataStore {
43-
private _tableName: string = '';
44-
private _ddb: DynamoDBDocumentClient | null = null;
45-
private static _instance: DataStore | null = null;
46-
47-
/** @internal Test hook: inject a document client for unit tests */
48-
static _testDocumentClient: DynamoDBDocumentClient | null = null;
49-
/** @internal Test hook: inject logMRTError for unit tests */
50-
static _testLogMRTError: ((namespace: string, err: unknown, context?: Record<string, unknown>) => void) | null = null;
51-
52-
private constructor() {
53-
// Private constructor for singleton; use DataStore.getDataStore() instead.
54-
}
55-
56-
/**
57-
* Get or create a DynamoDB document client (for abstraction of attribute values).
58-
*
59-
* @private
60-
* @returns The DynamoDB document client
61-
* @throws {DataStoreUnavailableError} The data store is unavailable
62-
*/
63-
private getClient(): DynamoDBDocumentClient {
64-
if (!this.isDataStoreAvailable()) {
65-
throw new DataStoreUnavailableError('The data store is unavailable.');
66-
}
67-
68-
if (DataStore._testDocumentClient) {
69-
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
70-
return DataStore._testDocumentClient;
71-
}
72-
73-
if (!this._ddb) {
74-
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
75-
this._ddb = DynamoDBDocumentClient.from(
76-
new DynamoDBClient({
77-
region: process.env.AWS_REGION,
78-
}),
79-
);
80-
}
81-
82-
return this._ddb;
83-
}
84-
85-
/**
86-
* Get or create the singleton DataStore instance.
87-
*
88-
* @returns The singleton DataStore instance
89-
*/
90-
static getDataStore(): DataStore {
91-
if (!DataStore._instance) {
92-
DataStore._instance = new DataStore();
93-
}
94-
return DataStore._instance;
95-
}
96-
97-
/**
98-
* Whether the data store can be used in the current environment.
99-
*
100-
* @returns true if the data store is available, false otherwise
101-
*/
102-
isDataStoreAvailable(): boolean {
103-
return Boolean(process.env.AWS_REGION && process.env.MOBIFY_PROPERTY_ID && process.env.DEPLOY_TARGET);
104-
}
105-
106-
/**
107-
* Fetch an entry from the data store.
108-
*
109-
* @param key The data store entry's key
110-
* @returns An object containing the entry's key and value
111-
* @throws {DataStoreUnavailableError} The data store is unavailable
112-
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
113-
* @throws {DataStoreServiceError} An internal error occurred
114-
*/
115-
async getEntry(key: string): Promise<Record<string, unknown> | undefined> {
116-
if (!this.isDataStoreAvailable()) {
117-
throw new DataStoreUnavailableError('The data store is unavailable.');
118-
}
119-
120-
const ddb = this.getClient();
121-
let response: GetCommandOutput;
122-
try {
123-
response = await ddb.send(
124-
new GetCommand({
125-
TableName: this._tableName,
126-
Key: {
127-
projectEnvironment: `${process.env.MOBIFY_PROPERTY_ID} ${process.env.DEPLOY_TARGET}`,
128-
key,
129-
},
130-
}),
131-
);
132-
} catch (error) {
133-
const logFn = DataStore._testLogMRTError ?? logMRTError;
134-
logFn('data_store', error, {key, tableName: this._tableName});
135-
throw new DataStoreServiceError('Data store request failed.');
136-
}
137-
138-
if (!response.Item?.value) {
139-
throw new DataStoreNotFoundError(`Data store entry '${key}' not found.`);
140-
}
141-
142-
return {key, value: response.Item.value};
143-
}
144-
}
11+
export * from '../data-store/index.js';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* SPDX-License-Identifier: Apache-2
4+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
export * from './middleware.js';
8+
export {type ProxyConfig} from '../utils/configure-proxying.js';

packages/mrt-utilities/src/middleware/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
55
*/
66

7-
export * from './data-store.js';
8-
export * from './middleware.js';
9-
export {type ProxyConfig} from '../utils/configure-proxying.js';
7+
export * from '../data-store/index.js';
8+
export * from './express.js';

packages/mrt-utilities/tsconfig.cjs.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"extends": "./tsconfig.json",
33
"compilerOptions": {
4-
"module": "Node16",
5-
"moduleResolution": "Node16",
4+
"module": "CommonJS",
5+
"moduleResolution": "Node",
66
"outDir": "dist/cjs",
77
"rootDir": "src",
88
"verbatimModuleSyntax": false

0 commit comments

Comments
 (0)