@attlaz/client 1.98.0 → 1.100.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.
package/dist/Client.d.ts CHANGED
@@ -21,6 +21,7 @@ import { HealthAlertEndpoint } from './Service/HealthAlertEndpoint.js';
21
21
  import { InboxEndpoint } from './Service/InboxEndpoint.js';
22
22
  import { LogEndpoint } from './Logging/Service/LogEndpoint.js';
23
23
  import { PlatformEndpoint } from './Service/PlatformEndpoint.js';
24
+ import { TimezoneEndpoint } from './Service/TimezoneEndpoint.js';
24
25
  import { PlatformLanguageEndpoint } from './Service/PlatformLanguageEndpoint.js';
25
26
  import { ProjectEndpoint } from './Service/ProjectEndpoint.js';
26
27
  import { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
@@ -102,6 +103,7 @@ export declare class Client {
102
103
  getConfigEndpoint(): ConfigEndpoint;
103
104
  getInboxEndpoint(): InboxEndpoint;
104
105
  getPlatformEndpoint(): PlatformEndpoint;
106
+ getTimezoneEndpoint(): TimezoneEndpoint;
105
107
  getSubscriberEndpoint(): SubscriberEndpoint;
106
108
  getLogEndPoint(): LogEndpoint;
107
109
  getFlowEndpoint(): FlowEndpoint;
package/dist/Client.js CHANGED
@@ -21,6 +21,7 @@ import { HealthAlertEndpoint } from './Service/HealthAlertEndpoint.js';
21
21
  import { InboxEndpoint } from './Service/InboxEndpoint.js';
22
22
  import { LogEndpoint } from './Logging/Service/LogEndpoint.js';
23
23
  import { PlatformEndpoint } from './Service/PlatformEndpoint.js';
24
+ import { TimezoneEndpoint } from './Service/TimezoneEndpoint.js';
24
25
  import { PlatformLanguageEndpoint } from './Service/PlatformLanguageEndpoint.js';
25
26
  import { ProjectEndpoint } from './Service/ProjectEndpoint.js';
26
27
  import { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
@@ -77,6 +78,7 @@ export class Client {
77
78
  InboxEndpoint,
78
79
  LogEndpoint,
79
80
  PlatformEndpoint,
81
+ TimezoneEndpoint,
80
82
  PlatformLanguageEndpoint,
81
83
  ProjectEndpoint,
82
84
  ProjectEnvironmentEndpoint,
@@ -217,6 +219,9 @@ export class Client {
217
219
  getPlatformEndpoint() {
218
220
  return this.getEndpoint('platform', this.Store.PlatformEndpoint);
219
221
  }
222
+ getTimezoneEndpoint() {
223
+ return this.getEndpoint('timezone', this.Store.TimezoneEndpoint);
224
+ }
220
225
  getSubscriberEndpoint() {
221
226
  return this.getEndpoint('subscriber', this.Store.SubscriberEndpoint);
222
227
  }
@@ -1,5 +1,6 @@
1
1
  import { State } from '../State.js';
2
2
  import { ComponentKind } from './ComponentKind.js';
3
+ import { ComponentVersion } from './ComponentVersion.js';
3
4
  /** One thing the platform builds and deploys: an api, a service, or the runner. */
4
5
  export declare class Component {
5
6
  id: string;
@@ -12,6 +13,12 @@ export declare class Component {
12
13
  * component is not managed this way, never "deploy nothing".
13
14
  */
14
15
  activeVersionId: string | null;
16
+ /**
17
+ * The active version itself, present only when requested with `?expand=active_version`.
18
+ * Null both when nothing is promoted and when the caller did not ask for it — check
19
+ * `activeVersionId` to tell those apart.
20
+ */
21
+ activeVersion: ComponentVersion | null;
15
22
  createdAt: Date;
16
23
  updatedAt: Date;
17
24
  state: State;
@@ -10,6 +10,12 @@ export class Component {
10
10
  * component is not managed this way, never "deploy nothing".
11
11
  */
12
12
  activeVersionId;
13
+ /**
14
+ * The active version itself, present only when requested with `?expand=active_version`.
15
+ * Null both when nothing is promoted and when the caller did not ask for it — check
16
+ * `activeVersionId` to tell those apart.
17
+ */
18
+ activeVersion = null;
13
19
  createdAt;
14
20
  updatedAt;
15
21
  state;
@@ -0,0 +1,18 @@
1
+ import { ApiRecord } from './ApiRecord.js';
2
+ /**
3
+ * A timezone the platform accepts, as reported by the API.
4
+ *
5
+ * The list comes from the server rather than from the browser's own `Intl`: the two run different
6
+ * tz database versions, so a browser-built list can offer a zone the API then rejects, or spell one
7
+ * differently from the name the API stores.
8
+ */
9
+ export declare class Timezone {
10
+ name: string;
11
+ /**
12
+ * Offset from UTC in minutes at the moment the API answered — positive east of Greenwich, and
13
+ * not a whole number of hours everywhere (Asia/Katmandu is 345). It follows daylight saving, so
14
+ * it is a value to display, never one to store or to compare zones by.
15
+ */
16
+ utcOffsetMinutes: number;
17
+ static parse(raw: ApiRecord): Timezone;
18
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A timezone the platform accepts, as reported by the API.
3
+ *
4
+ * The list comes from the server rather than from the browser's own `Intl`: the two run different
5
+ * tz database versions, so a browser-built list can offer a zone the API then rejects, or spell one
6
+ * differently from the name the API stores.
7
+ */
8
+ export class Timezone {
9
+ name;
10
+ /**
11
+ * Offset from UTC in minutes at the moment the API answered — positive east of Greenwich, and
12
+ * not a whole number of hours everywhere (Asia/Katmandu is 345). It follows daylight saving, so
13
+ * it is a value to display, never one to store or to compare zones by.
14
+ */
15
+ utcOffsetMinutes;
16
+ static parse(raw) {
17
+ const timezone = new Timezone();
18
+ timezone.name = raw.name;
19
+ timezone.utcOffsetMinutes = raw.utc_offset_minutes;
20
+ return timezone;
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Why a password was rejected. `null` means it was accepted.
3
+ *
4
+ * Mirrors the server's `PasswordCheckResult` — duplicated here rather than imported, per the client's
5
+ * standing rule that it does not depend on Core.
6
+ */
7
+ export type PasswordCheckCode = 'password_missing' | 'password_not_long_enough' | 'password_too_long' | 'password_repeat_match' | 'password_general_match' | 'password_too_weak';
8
+ export declare class PasswordCheck {
9
+ /** Null when the password is acceptable. */
10
+ code: PasswordCheckCode | null;
11
+ /** Human-readable reason, ready to display. Null when the password is acceptable. */
12
+ error: string | null;
13
+ /**
14
+ * zxcvbn score, 0 (weakest) to 4 (strongest). Null when the password was rejected before it was
15
+ * scored — an empty or out-of-bounds password never reaches the estimator.
16
+ *
17
+ * Treat it as a display hint, not a policy: the server owns the threshold, and a low score with
18
+ * `code === null` still means accepted.
19
+ */
20
+ strength: number | null;
21
+ isAcceptable(): boolean;
22
+ }
@@ -0,0 +1,17 @@
1
+ export class PasswordCheck {
2
+ /** Null when the password is acceptable. */
3
+ code = null;
4
+ /** Human-readable reason, ready to display. Null when the password is acceptable. */
5
+ error = null;
6
+ /**
7
+ * zxcvbn score, 0 (weakest) to 4 (strongest). Null when the password was rejected before it was
8
+ * scored — an empty or out-of-bounds password never reaches the estimator.
9
+ *
10
+ * Treat it as a display hint, not a policy: the server owns the threshold, and a low score with
11
+ * `code === null` still means accepted.
12
+ */
13
+ strength = null;
14
+ isAcceptable() {
15
+ return this.code === null;
16
+ }
17
+ }
@@ -1,4 +1,5 @@
1
1
  import { Component } from '../Model/Component/Component.js';
2
+ import { ComponentKind } from '../Model/Component/ComponentKind.js';
2
3
  import { ComponentStatus } from '../Model/Component/ComponentStatus.js';
3
4
  import { ComponentVersion } from '../Model/Component/ComponentVersion.js';
4
5
  import { CursorPagination } from '../Model/Pagination/CursorPagination.js';
@@ -11,7 +12,13 @@ import { Endpoint } from './Endpoint.js';
11
12
  * Root-only, reads included.
12
13
  */
13
14
  export declare class ComponentEndpoint extends Endpoint {
14
- getAll(pagination?: CursorPagination | null): Promise<CollectionResult<Component>>;
15
+ /**
16
+ * @param expand pass `['active_version']` to receive the promoted version inline instead of
17
+ * just its id — one call rather than one per component.
18
+ * @param kinds restrict to these kinds; a collection, so apis and services can be fetched
19
+ * together without the runner.
20
+ */
21
+ getAll(pagination?: CursorPagination | null, expand?: string[] | null, kinds?: ComponentKind[] | null): Promise<CollectionResult<Component>>;
15
22
  getVersions(componentId: string, pagination?: CursorPagination | null): Promise<CollectionResult<ComponentVersion>>;
16
23
  /** Observed state for every component at once — one call, not one per component. */
17
24
  getStatus(pagination?: CursorPagination | null): Promise<CollectionResult<ComponentStatus>>;
@@ -14,9 +14,21 @@ import { Endpoint } from './Endpoint.js';
14
14
  * Root-only, reads included.
15
15
  */
16
16
  export class ComponentEndpoint extends Endpoint {
17
- async getAll(pagination = null) {
17
+ /**
18
+ * @param expand pass `['active_version']` to receive the promoted version inline instead of
19
+ * just its id — one call rather than one per component.
20
+ * @param kinds restrict to these kinds; a collection, so apis and services can be fetched
21
+ * together without the runner.
22
+ */
23
+ async getAll(pagination = null, expand = null, kinds = null) {
18
24
  const queryString = new QueryString('/system/components');
19
25
  queryString.addPagination(pagination);
26
+ if (kinds !== null && kinds.length > 0) {
27
+ queryString.set('kind', kinds);
28
+ }
29
+ if (expand !== null && expand.length > 0) {
30
+ queryString.set('expand', expand);
31
+ }
20
32
  return await this.requestCollection(queryString, ComponentEndpoint.parseComponent, null, 'GET');
21
33
  }
22
34
  async getVersions(componentId, pagination = null) {
@@ -36,7 +48,9 @@ export class ComponentEndpoint extends Endpoint {
36
48
  */
37
49
  async setActiveVersion(componentId, versionId) {
38
50
  const queryString = new QueryString(path('/system/components/:componentId/active-version', { componentId }));
39
- await this.requestObject(queryString, { version_id: versionId }, (raw) => raw, 'POST');
51
+ // `_formatted` because formatParameters() strips a trailing `_id` from body keys, which
52
+ // would turn `version_id` into `version` — a key the endpoint does not read.
53
+ await this.requestObject(queryString, { version_id: versionId, _formatted: true }, (raw) => raw, 'POST');
40
54
  }
41
55
  static parseComponent(raw) {
42
56
  const component = new Component();
@@ -44,7 +58,16 @@ export class ComponentEndpoint extends Endpoint {
44
58
  component.name = raw.name;
45
59
  component.kind = ComponentKind.fromString(raw.kind);
46
60
  component.imageRepository = raw.image_repository;
47
- component.activeVersionId = raw.active_version ?? null;
61
+ // `active_version` is either the id or, when expanded, the version itself.
62
+ const activeVersion = raw.active_version ?? null;
63
+ if (activeVersion !== null && typeof activeVersion === 'object') {
64
+ component.activeVersion = ComponentEndpoint.parseVersion(activeVersion);
65
+ component.activeVersionId = component.activeVersion.id;
66
+ }
67
+ else {
68
+ component.activeVersionId = activeVersion;
69
+ component.activeVersion = null;
70
+ }
48
71
  component.createdAt = Utils.parseRawDate(raw.created_at);
49
72
  component.updatedAt = Utils.parseRawDate(raw.updated_at);
50
73
  component.state = State.fromString(raw.state);
@@ -0,0 +1,13 @@
1
+ import { CollectionResult } from '../Model/Result/CollectionResult.js';
2
+ import { Timezone } from '../Model/Timezone.js';
3
+ import { Endpoint } from './Endpoint.js';
4
+ export declare class TimezoneEndpoint extends Endpoint {
5
+ /**
6
+ * Every timezone a schedule or a user profile can be set to.
7
+ *
8
+ * Deliberately not paginated: this is a fixed reference list of a few hundred entries owned by
9
+ * the platform's runtime, not a growing collection, and a picker needs all of it to filter
10
+ * against.
11
+ */
12
+ getAll(): Promise<CollectionResult<Timezone>>;
13
+ }
@@ -0,0 +1,15 @@
1
+ import { QueryString } from '../Http/Data/QueryString.js';
2
+ import { Timezone } from '../Model/Timezone.js';
3
+ import { Endpoint } from './Endpoint.js';
4
+ export class TimezoneEndpoint extends Endpoint {
5
+ /**
6
+ * Every timezone a schedule or a user profile can be set to.
7
+ *
8
+ * Deliberately not paginated: this is a fixed reference list of a few hundred entries owned by
9
+ * the platform's runtime, not a growing collection, and a picker needs all of it to filter
10
+ * against.
11
+ */
12
+ async getAll() {
13
+ return await this.requestCollection(new QueryString('/timezones'), Timezone.parse);
14
+ }
15
+ }
@@ -1,6 +1,7 @@
1
1
  import { UserAuthProvider } from '../Model/User/UserAuthProvider.js';
2
2
  import { User } from '../Model/User/User.js';
3
3
  import { UserSummary } from '../Model/User/UserSummary.js';
4
+ import { PasswordCheck } from '../Model/User/PasswordCheck.js';
4
5
  import { CollectionResult } from '../Model/Result/CollectionResult.js';
5
6
  import { Endpoint } from './Endpoint.js';
6
7
  import { UserNotificationSession } from '../Model/User/UserNotificationSession.js';
@@ -23,6 +24,18 @@ export declare class UserEndpoint extends Endpoint {
23
24
  update(user: User): Promise<User>;
24
25
  create(user: User, password: string, invitationCode?: string | null): Promise<User>;
25
26
  requestResetPassword(email: string): Promise<boolean>;
27
+ /**
28
+ * Ask the server whether a password is acceptable, without setting it.
29
+ *
30
+ * Unauthenticated — signup calls this before an account, and therefore a token, exists. Pass the
31
+ * email when it is known so the server can price the password against the user's own details;
32
+ * `stijn2026` for `stijn@echron.com` scores far lower with it than without.
33
+ *
34
+ * Advisory only. The same rules run again when the password is actually set, so a caller must not
35
+ * treat acceptance here as a guarantee.
36
+ */
37
+ checkPassword(password: string, email?: string | null): Promise<PasswordCheck>;
38
+ private static parsePasswordCheck;
26
39
  resetPassword(code: string, password: string): Promise<boolean>;
27
40
  createWebPush(session: UserNotificationSession): Promise<boolean>;
28
41
  getSessions(pagination: CursorPagination): Promise<CollectionResult<UserNotificationSession>>;
@@ -2,6 +2,7 @@ import { path } from '../Http/Data/Path.js';
2
2
  import { UserAuthProvider } from '../Model/User/UserAuthProvider.js';
3
3
  import { User } from '../Model/User/User.js';
4
4
  import { UserSummary } from '../Model/User/UserSummary.js';
5
+ import { PasswordCheck } from '../Model/User/PasswordCheck.js';
5
6
  import { Endpoint } from './Endpoint.js';
6
7
  import { UserNotificationSession } from '../Model/User/UserNotificationSession.js';
7
8
  import { QueryString } from '../Http/Data/QueryString.js';
@@ -115,6 +116,42 @@ export class UserEndpoint extends Endpoint {
115
116
  throw e;
116
117
  }
117
118
  }
119
+ /**
120
+ * Ask the server whether a password is acceptable, without setting it.
121
+ *
122
+ * Unauthenticated — signup calls this before an account, and therefore a token, exists. Pass the
123
+ * email when it is known so the server can price the password against the user's own details;
124
+ * `stijn2026` for `stijn@echron.com` scores far lower with it than without.
125
+ *
126
+ * Advisory only. The same rules run again when the password is actually set, so a caller must not
127
+ * treat acceptance here as a guarantee.
128
+ */
129
+ async checkPassword(password, email = null) {
130
+ try {
131
+ const url = '/users/check-password';
132
+ const parameters = email === null ? { password } : { password, email };
133
+ const result = await this.requestObject(url, parameters, UserEndpoint.parsePasswordCheck, 'POST', false);
134
+ const check = result.getData();
135
+ if (check === null) {
136
+ throw new Error('Unable to check password: wrong response from API');
137
+ }
138
+ return check;
139
+ }
140
+ catch (e) {
141
+ if (this.httpClient.isDebugEnabled()) {
142
+ // Never log the password itself.
143
+ console.error('Failed to check password: ', e);
144
+ }
145
+ throw e;
146
+ }
147
+ }
148
+ static parsePasswordCheck(raw) {
149
+ const check = new PasswordCheck();
150
+ check.code = (raw.code ?? null);
151
+ check.error = (raw.error ?? null);
152
+ check.strength = (raw.strength ?? null);
153
+ return check;
154
+ }
118
155
  async resetPassword(code, password) {
119
156
  try {
120
157
  const url = path('/users/resetpassword/:code', { code });
package/dist/index.d.ts CHANGED
@@ -161,6 +161,7 @@ export { SearchResult } from './Model/Search/SearchResult.js';
161
161
  export { Trigger } from './Model/Trigger/Trigger.js';
162
162
  /** Worker* */
163
163
  export { Platform } from './Model/Worker/Platform.js';
164
+ export { Timezone } from './Model/Timezone.js';
164
165
  export { PlatformImage } from './Model/Worker/PlatformImage.js';
165
166
  export { InfrastructureConfiguration } from './Model/Worker/InfrastructureConfiguration.js';
166
167
  /** Deployment * */
@@ -208,6 +209,8 @@ export { UserAction } from './Model/User/UserAction.js';
208
209
  export type { UserActionFilter } from './Model/User/UserActionFilter.js';
209
210
  export { UserSummary } from './Model/User/UserSummary.js';
210
211
  export type { UserSummaryType } from './Model/User/UserSummary.js';
212
+ export { PasswordCheck } from './Model/User/PasswordCheck.js';
213
+ export type { PasswordCheckCode } from './Model/User/PasswordCheck.js';
211
214
  export { RunnerImage } from './Model/Runner/RunnerImage.js';
212
215
  export { Component } from './Model/Component/Component.js';
213
216
  export { ComponentKind } from './Model/Component/ComponentKind.js';
@@ -227,6 +230,7 @@ export { LogEndpoint } from './Logging/Service/LogEndpoint.js';
227
230
  export { WorkspaceMemberEndpoint } from './Service/WorkspaceMemberEndpoint.js';
228
231
  export { InboxEndpoint } from './Service/InboxEndpoint.js';
229
232
  export { PlatformEndpoint } from './Service/PlatformEndpoint.js';
233
+ export { TimezoneEndpoint } from './Service/TimezoneEndpoint.js';
230
234
  export { CodeDeployEndpoint } from './Service/CodeDeployEndpoint.js';
231
235
  export { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
232
236
  export { SearchEndpoint } from './Service/SearchEndpoint.js';
package/dist/index.js CHANGED
@@ -127,6 +127,7 @@ export { SearchResult } from './Model/Search/SearchResult.js';
127
127
  export { Trigger } from './Model/Trigger/Trigger.js';
128
128
  /** Worker* */
129
129
  export { Platform } from './Model/Worker/Platform.js';
130
+ export { Timezone } from './Model/Timezone.js';
130
131
  export { PlatformImage } from './Model/Worker/PlatformImage.js';
131
132
  export { InfrastructureConfiguration } from './Model/Worker/InfrastructureConfiguration.js';
132
133
  /** Deployment * */
@@ -171,6 +172,7 @@ export { User } from './Model/User/User.js';
171
172
  export { UserAuthProvider } from './Model/User/UserAuthProvider.js';
172
173
  export { UserAction } from './Model/User/UserAction.js';
173
174
  export { UserSummary } from './Model/User/UserSummary.js';
175
+ export { PasswordCheck } from './Model/User/PasswordCheck.js';
174
176
  export { RunnerImage } from './Model/Runner/RunnerImage.js';
175
177
  export { Component } from './Model/Component/Component.js';
176
178
  export { ComponentKind } from './Model/Component/ComponentKind.js';
@@ -190,6 +192,7 @@ export { LogEndpoint } from './Logging/Service/LogEndpoint.js';
190
192
  export { WorkspaceMemberEndpoint } from './Service/WorkspaceMemberEndpoint.js';
191
193
  export { InboxEndpoint } from './Service/InboxEndpoint.js';
192
194
  export { PlatformEndpoint } from './Service/PlatformEndpoint.js';
195
+ export { TimezoneEndpoint } from './Service/TimezoneEndpoint.js';
193
196
  export { CodeDeployEndpoint } from './Service/CodeDeployEndpoint.js';
194
197
  export { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
195
198
  export { SearchEndpoint } from './Service/SearchEndpoint.js';
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.97.0";
1
+ export declare const VERSION = "1.99.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.97.0";
1
+ export const VERSION = "1.99.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attlaz/client",
3
- "version": "1.98.0",
3
+ "version": "1.100.0",
4
4
  "description": "Javascript Client to access Attlaz API",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -37,14 +37,15 @@
37
37
  "license": "MIT",
38
38
  "scripts": {
39
39
  "prebuild": "npm run setversion",
40
- "prepare": "npm run clean && tsc --project tsconfig.production.json && npm run setversion",
41
- "build": "npm run clean && tsc",
40
+ "prepare": "npm run clean && tsc --project tsconfig.build.json && npm run setversion",
41
+ "build": "npm run clean && tsc -p tsconfig.build.json",
42
42
  "setversion": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
43
43
  "clean": "rimraf dist",
44
44
  "scan": "docker run -v .:/path zricethezav/gitleaks:latest detect --source=\"/path\" ",
45
- "test": "jest",
46
- "test:unit": "jest --config jest.config.unit.js",
47
- "typecheck": "tsc --project tsconfig.json --noEmit"
45
+ "test": "vitest run --mode unit",
46
+ "typecheck": "tsc --project tsconfig.json --noEmit",
47
+ "test-integration": "vitest run --mode integration",
48
+ "test-live": "vitest --mode unit"
48
49
  },
49
50
  "publishConfig": {
50
51
  "access": "public",
@@ -52,8 +53,7 @@
52
53
  "registry": "https://registry.npmjs.org"
53
54
  },
54
55
  "devDependencies": {
55
- "@types/jest": "^30.0.0",
56
- "@types/node": "^26.1.2",
56
+ "@types/node": "^26.2.0",
57
57
  "@typescript-eslint/eslint-plugin": "^8.59.3",
58
58
  "@typescript-eslint/parser": "^8.59.3",
59
59
  "eslint": "^9.39.5",
@@ -63,10 +63,9 @@
63
63
  "eslint-plugin-jsdoc": "^62.9.0",
64
64
  "eslint-plugin-prefer-arrow": "^1.2.3",
65
65
  "eslint-plugin-promise": "^7.3.0",
66
- "jest": "^30.4.2",
67
66
  "rimraf": "^6.1.3",
68
- "ts-jest": "^29.4.12",
69
- "typescript": "^6.0.3"
67
+ "typescript": "^6.0.3",
68
+ "vitest": "^4.1.10"
70
69
  },
71
70
  "directories": {
72
71
  "test": "test"