@hubspot/local-dev-lib 0.7.9-experimental.0 → 0.7.9-experimental.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/api/projects.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HubSpotPromise, QueryParams } from '../types/Http.js';
2
- import { Project, FetchProjectResponse, UploadProjectResponse, ProjectSettings, FetchPlatformVersionResponse, WarnLogsResponse, UploadIRResponse, Release, FetchListReleasesResponse, AutoReleaseResponse, AutoReleaseStatusResponse } from '../types/Project.js';
2
+ import { Project, FetchProjectResponse, UploadProjectResponse, ProjectSettings, FetchPlatformVersionResponse, WarnLogsResponse, UploadIRResponse } from '../types/Project.js';
3
3
  import { Build, FetchProjectBuildsResponse } from '../types/Build.js';
4
4
  import { ComponentStructureResponse, ProjectComponentsMetadata } from '../types/ComponentStructure.js';
5
5
  import { Deploy, ProjectDeployResponse, ProjectDeployResponseV1, ProjectDeletionResponse } from '../types/Deploy.js';
@@ -39,11 +39,6 @@ export declare function deleteFileFromBuild(accountId: number, projectName: stri
39
39
  export declare function cancelStagedBuild(accountId: number, projectName: string): HubSpotPromise<void>;
40
40
  export declare function fetchBuildWarnLogs(accountId: number, projectName: string, buildId: number): HubSpotPromise<WarnLogsResponse>;
41
41
  export declare function fetchDeployWarnLogs(accountId: number, projectName: string, deployId: number): HubSpotPromise<WarnLogsResponse>;
42
- export declare function createRelease(accountId: number, projectName: string, buildId: number): HubSpotPromise<Release>;
43
- export declare function listReleases(accountId: number, projectName: string, params?: QueryParams): HubSpotPromise<FetchListReleasesResponse>;
44
- export declare function getReleaseInfo(accountId: number, projectName: string, releaseTag: string): HubSpotPromise<Release>;
45
- export declare function triggerAutoRelease(accountId: number, projectId: number, buildId: number, targetPortalId: number): HubSpotPromise<AutoReleaseResponse>;
46
- export declare function getAutoReleaseStatus(accountId: number, projectId: number, targetPortalId: number, expectedReleaseTag: string, appId: number): HubSpotPromise<AutoReleaseStatusResponse>;
47
42
  /**
48
43
  * @deprecated
49
44
  */
package/api/projects.js CHANGED
@@ -215,44 +215,6 @@ export function fetchDeployWarnLogs(accountId, projectName, deployId) {
215
215
  url: `${PROJECTS_LOGS_API_PATH}/logs/projects/${encodeURIComponent(projectName)}/deploys/${deployId}/combined/warn`,
216
216
  });
217
217
  }
218
- export function createRelease(accountId, projectName, buildId) {
219
- return http.post(accountId, {
220
- url: `${PROJECTS_API_PATH}/${encodeURIComponent(projectName)}/releases`,
221
- data: { buildId },
222
- });
223
- }
224
- export function listReleases(accountId, projectName, params = {}) {
225
- return http.get(accountId, {
226
- url: `${PROJECTS_API_PATH}/${encodeURIComponent(projectName)}/releases`,
227
- params,
228
- });
229
- }
230
- export function getReleaseInfo(accountId, projectName, releaseTag) {
231
- return http.get(accountId, {
232
- url: `${PROJECTS_API_PATH}/${encodeURIComponent(projectName)}/releases/${encodeURIComponent(releaseTag)}`,
233
- });
234
- }
235
- export function triggerAutoRelease(accountId, projectId, buildId, targetPortalId) {
236
- return http.post(accountId, {
237
- url: `${PROJECTS_DEPLOY_API_PATH}/auto-release`,
238
- data: {
239
- projectId,
240
- buildId,
241
- targetPortalId,
242
- },
243
- });
244
- }
245
- export function getAutoReleaseStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId) {
246
- return http.get(accountId, {
247
- url: `${PROJECTS_DEPLOY_API_PATH}/auto-release/status`,
248
- params: {
249
- projectId,
250
- targetPortalId,
251
- expectedReleaseTag,
252
- appId,
253
- },
254
- });
255
- }
256
218
  /**
257
219
  * @deprecated
258
220
  */
@@ -0,0 +1,26 @@
1
+ export type LogBufferOptions = {
2
+ byteLimit?: number;
3
+ };
4
+ export type WriteBufferedLogsOptions = {
5
+ dir: string;
6
+ filenamePrefix: string;
7
+ maxFiles?: number;
8
+ };
9
+ /**
10
+ * In-memory ring buffer of log entries with a byte cap. Designed to be
11
+ * composed into a logger so that recent log output can be flushed to a file
12
+ * after a failure. Each record is timestamped and tagged with the level the
13
+ * caller provides.
14
+ */
15
+ export declare class LogBuffer {
16
+ private entries;
17
+ private bytes;
18
+ private byteLimit;
19
+ constructor(options?: LogBufferOptions);
20
+ private static entrySize;
21
+ private trimToByteLimit;
22
+ record(level: string, args: unknown[]): void;
23
+ view(): string;
24
+ flush(): string;
25
+ writeToFile(options: WriteBufferedLogsOptions): string | null;
26
+ }
@@ -0,0 +1,95 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ // 128 MiB
4
+ const DEFAULT_BYTE_LIMIT = 128 * 1024 * 1024;
5
+ const DEFAULT_MAX_LOG_FILES = 3;
6
+ function sanitizeFilenamePart(name) {
7
+ return name.replace(/[^a-zA-Z0-9._-]+/g, '-');
8
+ }
9
+ function timestampForFilename() {
10
+ const d = new Date();
11
+ const pad = (n, w = 2) => String(n).padStart(w, '0');
12
+ const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
13
+ const time = `${pad(d.getHours())}-${pad(d.getMinutes())}-${pad(d.getSeconds())}`;
14
+ return `${date}T${time}-${pad(d.getMilliseconds(), 3)}`;
15
+ }
16
+ function rotateLogFiles(dir, maxFiles) {
17
+ const entries = fs
18
+ .readdirSync(dir)
19
+ .map(name => {
20
+ const full = path.join(dir, name);
21
+ const stat = fs.statSync(full);
22
+ return { full, mtimeMs: stat.mtimeMs, isFile: stat.isFile() };
23
+ })
24
+ .filter(entry => entry.isFile)
25
+ .sort((a, b) => a.mtimeMs - b.mtimeMs);
26
+ while (entries.length >= maxFiles) {
27
+ const oldest = entries.shift();
28
+ if (oldest) {
29
+ fs.unlinkSync(oldest.full);
30
+ }
31
+ }
32
+ }
33
+ /**
34
+ * In-memory ring buffer of log entries with a byte cap. Designed to be
35
+ * composed into a logger so that recent log output can be flushed to a file
36
+ * after a failure. Each record is timestamped and tagged with the level the
37
+ * caller provides.
38
+ */
39
+ export class LogBuffer {
40
+ entries = [];
41
+ bytes = 0;
42
+ byteLimit;
43
+ constructor(options = {}) {
44
+ this.byteLimit = options.byteLimit ?? DEFAULT_BYTE_LIMIT;
45
+ }
46
+ // +1 accounts for the '\n' separator that join('\n') will insert between entries.
47
+ static entrySize(entry) {
48
+ return Buffer.byteLength(entry, 'utf8') + 1;
49
+ }
50
+ // Drop oldest entries until under the cap. Always retains at least the most
51
+ // recent entry so a single oversized message is still captured.
52
+ trimToByteLimit() {
53
+ while (this.bytes > this.byteLimit && this.entries.length > 1) {
54
+ const removed = this.entries.shift();
55
+ this.bytes -= LogBuffer.entrySize(removed);
56
+ }
57
+ }
58
+ record(level, args) {
59
+ const message = args.map(arg => String(arg)).join(' ');
60
+ const entry = `[${new Date().toISOString()}] [${level}] ${message}`;
61
+ this.entries.push(entry);
62
+ this.bytes += LogBuffer.entrySize(entry);
63
+ this.trimToByteLimit();
64
+ }
65
+ view() {
66
+ return this.entries.join('\n');
67
+ }
68
+ flush() {
69
+ const out = this.entries.join('\n');
70
+ this.entries.length = 0;
71
+ this.bytes = 0;
72
+ return out;
73
+ }
74
+ // Flush the buffer to a rotating log file. Always clears the buffer (even
75
+ // on write failure). Returns the written file path on success, or null if
76
+ // the buffer was empty or the write failed.
77
+ writeToFile(options) {
78
+ const { dir, filenamePrefix, maxFiles = DEFAULT_MAX_LOG_FILES } = options;
79
+ const contents = this.flush();
80
+ if (!contents) {
81
+ return null;
82
+ }
83
+ try {
84
+ fs.mkdirSync(dir, { recursive: true });
85
+ rotateLogFiles(dir, maxFiles);
86
+ const filename = `${sanitizeFilenamePart(filenamePrefix)}-${timestampForFilename()}.log`;
87
+ const filePath = path.join(dir, filename);
88
+ fs.writeFileSync(filePath, contents, 'utf8');
89
+ return filePath;
90
+ }
91
+ catch (_e) {
92
+ return null;
93
+ }
94
+ }
95
+ }
package/lib/logger.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { type ChalkInstance } from 'chalk';
2
+ import { WriteBufferedLogsOptions } from './LogBuffer.js';
3
+ export type { WriteBufferedLogsOptions };
2
4
  export declare const LOG_LEVEL: {
3
5
  NONE: number;
4
6
  DEBUG: number;
@@ -50,5 +52,7 @@ export declare const logger: {
50
52
  debug(...args: any[]): void;
51
53
  group(...args: any[]): void;
52
54
  groupEnd(): void;
55
+ viewLogBuffer(): string;
56
+ flushLogBuffer(): string;
57
+ writeBufferedLogsToFile(options: WriteBufferedLogsOptions): string | null;
53
58
  };
54
- export {};
package/lib/logger.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import chalk from 'chalk';
3
3
  import { isUnicodeSupported } from './isUnicodeSupported.js';
4
+ import { LogBuffer } from './LogBuffer.js';
4
5
  export const LOG_LEVEL = {
5
6
  NONE: 0,
6
7
  DEBUG: 1,
@@ -127,41 +128,58 @@ export function getLogLevel() {
127
128
  return LOG_LEVEL.NONE;
128
129
  }
129
130
  }
131
+ const _logBuffer = new LogBuffer();
130
132
  export const logger = {
131
133
  error(...args) {
134
+ _logBuffer.record('ERROR', args);
132
135
  if (shouldLog(LOG_LEVEL.ERROR)) {
133
136
  currentLogger.error(...args);
134
137
  }
135
138
  },
136
139
  warn(...args) {
140
+ _logBuffer.record('WARN', args);
137
141
  if (shouldLog(LOG_LEVEL.WARN)) {
138
142
  currentLogger.warn(...args);
139
143
  }
140
144
  },
141
145
  log(...args) {
146
+ _logBuffer.record('LOG', args);
142
147
  if (shouldLog(LOG_LEVEL.LOG)) {
143
148
  currentLogger.log(...args);
144
149
  }
145
150
  },
146
151
  success(...args) {
152
+ _logBuffer.record('SUCCESS', args);
147
153
  if (shouldLog(LOG_LEVEL.LOG)) {
148
154
  currentLogger.success(...args);
149
155
  }
150
156
  },
151
157
  info(...args) {
158
+ _logBuffer.record('INFO', args);
152
159
  if (shouldLog(LOG_LEVEL.LOG)) {
153
160
  currentLogger.info(...args);
154
161
  }
155
162
  },
156
163
  debug(...args) {
164
+ _logBuffer.record('DEBUG', args);
157
165
  if (shouldLog(LOG_LEVEL.DEBUG)) {
158
166
  currentLogger.debug(...args);
159
167
  }
160
168
  },
161
169
  group(...args) {
170
+ _logBuffer.record('GROUP', args);
162
171
  currentLogger.group(...args);
163
172
  },
164
173
  groupEnd() {
165
174
  currentLogger.groupEnd();
166
175
  },
176
+ viewLogBuffer() {
177
+ return _logBuffer.view();
178
+ },
179
+ flushLogBuffer() {
180
+ return _logBuffer.flush();
181
+ },
182
+ writeBufferedLogsToFile(options) {
183
+ return _logBuffer.writeToFile(options);
184
+ },
167
185
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/local-dev-lib",
3
- "version": "0.7.9-experimental.0",
3
+ "version": "0.7.9-experimental.1",
4
4
  "type": "module",
5
5
  "description": "Provides library functionality for HubSpot local development tooling, including the HubSpot CLI",
6
6
  "files": [
@@ -1,8 +1,6 @@
1
1
  import type { Build } from './Build.js';
2
2
  import { GithubSourceData } from './Github.js';
3
3
  import { ProjectLog } from './ProjectLog.js';
4
- import { ValueOf } from './Utils.js';
5
- import { SUBBUILD_TYPES } from '../enums/build.js';
6
4
  export type Project = {
7
5
  createdAt: number;
8
6
  deletedAt: number;
@@ -50,31 +48,3 @@ export type FetchPlatformVersionResponse = {
50
48
  export type WarnLogsResponse = {
51
49
  logs: Array<ProjectLog>;
52
50
  };
53
- export type Release = {
54
- releaseTag: string;
55
- buildId: number;
56
- createdAt: string;
57
- components?: Array<{
58
- buildType: ValueOf<typeof SUBBUILD_TYPES>;
59
- buildName?: string;
60
- rootPath?: string;
61
- id?: string;
62
- }>;
63
- };
64
- export type FetchListReleasesResponse = {
65
- results: Array<Release>;
66
- paging: {
67
- next: {
68
- after: string;
69
- };
70
- };
71
- };
72
- export type AutoReleaseResponse = {
73
- releaseTag: string;
74
- status: string;
75
- appId: number;
76
- };
77
- export type AutoReleaseStatusResponse = {
78
- status: 'PENDING' | 'COMPLETE';
79
- currentReleaseTag?: string;
80
- };