@argos-ci/core 3.2.1 → 3.2.3
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/index.d.ts +69 -53
- package/dist/index.js +102 -41
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -125,88 +125,72 @@ interface components {
|
|
|
125
125
|
Sha1Hash: string;
|
|
126
126
|
/** @description SHA256 hash */
|
|
127
127
|
Sha256Hash: string;
|
|
128
|
-
/** @description Build */
|
|
129
|
-
Build: {
|
|
130
|
-
id: components["schemas"]["BuildId"];
|
|
131
|
-
/** @description The build number */
|
|
132
|
-
number: number;
|
|
133
|
-
/** @description The status of the build */
|
|
134
|
-
status: ("accepted" | "rejected") | ("no-changes" | "changes-detected") | ("expired" | "pending" | "progress" | "error" | "aborted");
|
|
135
|
-
/**
|
|
136
|
-
* Format: uri
|
|
137
|
-
* @description The URL of the build
|
|
138
|
-
*/
|
|
139
|
-
url: string;
|
|
140
|
-
/** @description The notification payload for the build */
|
|
141
|
-
notification: {
|
|
142
|
-
description: string;
|
|
143
|
-
context: string;
|
|
144
|
-
github: {
|
|
145
|
-
/** @enum {string} */
|
|
146
|
-
state: "pending" | "success" | "error" | "failure";
|
|
147
|
-
};
|
|
148
|
-
gitlab: {
|
|
149
|
-
/** @enum {string} */
|
|
150
|
-
state: "pending" | "running" | "success" | "failed" | "canceled";
|
|
151
|
-
};
|
|
152
|
-
} | null;
|
|
153
|
-
};
|
|
154
128
|
/**
|
|
155
129
|
* @description A unique identifier for the build
|
|
156
130
|
* @example 12345
|
|
157
131
|
*/
|
|
158
132
|
BuildId: string;
|
|
159
|
-
/** @description Error response */
|
|
160
|
-
Error: {
|
|
161
|
-
error: string;
|
|
162
|
-
details?: {
|
|
163
|
-
message: string;
|
|
164
|
-
}[];
|
|
165
|
-
};
|
|
166
|
-
/**
|
|
167
|
-
* @description A unique identifier for the build
|
|
168
|
-
* @example 12345
|
|
169
|
-
*/
|
|
170
|
-
buildId: string;
|
|
171
133
|
/** @description Screenshot input */
|
|
172
134
|
ScreenshotInput: {
|
|
173
135
|
key: string;
|
|
174
136
|
name: string;
|
|
175
137
|
baseName?: string | null;
|
|
176
138
|
metadata?: {
|
|
177
|
-
|
|
178
|
-
|
|
139
|
+
/** @description The URL of the page that was screenshotted */
|
|
140
|
+
url?: string | null;
|
|
141
|
+
/** @description An URL to an accessible preview of the screenshot */
|
|
142
|
+
previewUrl?: string | null;
|
|
179
143
|
viewport?: {
|
|
144
|
+
/** @description The width of the viewport */
|
|
180
145
|
width: number;
|
|
146
|
+
/** @description The height of the viewport */
|
|
181
147
|
height: number;
|
|
182
|
-
};
|
|
183
|
-
/** @
|
|
184
|
-
colorScheme?: "light" | "dark";
|
|
185
|
-
/** @
|
|
186
|
-
mediaType?: "screen" | "print";
|
|
187
|
-
test?: {
|
|
188
|
-
|
|
148
|
+
} | null;
|
|
149
|
+
/** @description The color scheme when the screenshot was taken */
|
|
150
|
+
colorScheme?: ("light" | "dark") | null;
|
|
151
|
+
/** @description The media type when the screenshot was taken */
|
|
152
|
+
mediaType?: ("screen" | "print") | null;
|
|
153
|
+
test?: ({
|
|
154
|
+
/** @description The unique identifier of the test */
|
|
155
|
+
id?: string | null;
|
|
156
|
+
/** @description The title of the test */
|
|
189
157
|
title: string;
|
|
158
|
+
/** @description The path of titles leading to the test */
|
|
190
159
|
titlePath: string[];
|
|
191
|
-
retries
|
|
192
|
-
|
|
193
|
-
|
|
160
|
+
/** @description The number of retries for the test */
|
|
161
|
+
retries?: number | null;
|
|
162
|
+
/** @description The current retry count */
|
|
163
|
+
retry?: number | null;
|
|
164
|
+
/** @description The repeat count for the test */
|
|
165
|
+
repeat?: number | null;
|
|
166
|
+
/** @description The location of the test in the source code */
|
|
194
167
|
location?: {
|
|
168
|
+
/** @description The file where the test is located */
|
|
195
169
|
file: string;
|
|
170
|
+
/** @description The line number in the file */
|
|
196
171
|
line: number;
|
|
172
|
+
/** @description The column number in the file */
|
|
197
173
|
column: number;
|
|
198
174
|
};
|
|
199
|
-
} | null;
|
|
175
|
+
} | null) | null;
|
|
200
176
|
browser?: {
|
|
177
|
+
/** @description The name of the browser */
|
|
201
178
|
name: string;
|
|
179
|
+
/** @description The version of the browser */
|
|
202
180
|
version: string;
|
|
203
|
-
};
|
|
181
|
+
} | null;
|
|
182
|
+
/** @description The automation library that generated the screenshot */
|
|
204
183
|
automationLibrary: {
|
|
184
|
+
/** @description The name of the automation library */
|
|
205
185
|
name: string;
|
|
186
|
+
/** @description The version of the automation library */
|
|
206
187
|
version: string;
|
|
207
188
|
};
|
|
189
|
+
/** @description The Argos SDK that generated the screenshot */
|
|
208
190
|
sdk: {
|
|
191
|
+
/** @description The name of the Argos SDK */
|
|
209
192
|
name: string;
|
|
193
|
+
/** @description The version of the Argos SDK */
|
|
210
194
|
version: string;
|
|
211
195
|
};
|
|
212
196
|
} | null;
|
|
@@ -224,6 +208,39 @@ interface components {
|
|
|
224
208
|
};
|
|
225
209
|
};
|
|
226
210
|
};
|
|
211
|
+
/** @description Build */
|
|
212
|
+
Build: {
|
|
213
|
+
id: components["schemas"]["BuildId"];
|
|
214
|
+
/** @description The build number */
|
|
215
|
+
number: number;
|
|
216
|
+
/** @description The status of the build */
|
|
217
|
+
status: ("accepted" | "rejected") | ("no-changes" | "changes-detected") | ("expired" | "pending" | "progress" | "error" | "aborted");
|
|
218
|
+
/**
|
|
219
|
+
* Format: uri
|
|
220
|
+
* @description The URL of the build
|
|
221
|
+
*/
|
|
222
|
+
url: string;
|
|
223
|
+
/** @description The notification payload for the build */
|
|
224
|
+
notification: {
|
|
225
|
+
description: string;
|
|
226
|
+
context: string;
|
|
227
|
+
github: {
|
|
228
|
+
/** @enum {string} */
|
|
229
|
+
state: "pending" | "success" | "error" | "failure";
|
|
230
|
+
};
|
|
231
|
+
gitlab: {
|
|
232
|
+
/** @enum {string} */
|
|
233
|
+
state: "pending" | "running" | "success" | "failed" | "canceled";
|
|
234
|
+
};
|
|
235
|
+
} | null;
|
|
236
|
+
};
|
|
237
|
+
/** @description Error response */
|
|
238
|
+
Error: {
|
|
239
|
+
error: string;
|
|
240
|
+
details?: {
|
|
241
|
+
message: string;
|
|
242
|
+
}[];
|
|
243
|
+
};
|
|
227
244
|
/** @description Project */
|
|
228
245
|
Project: {
|
|
229
246
|
id: string;
|
|
@@ -271,7 +288,6 @@ interface Config {
|
|
|
271
288
|
parallelTotal: number | null;
|
|
272
289
|
referenceBranch: string | null;
|
|
273
290
|
referenceCommit: string | null;
|
|
274
|
-
owner: string | null;
|
|
275
291
|
repository: string | null;
|
|
276
292
|
jobId: string | null;
|
|
277
293
|
runId: string | null;
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,14 @@ function branch() {
|
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
function getRepositoryURL() {
|
|
53
|
+
try {
|
|
54
|
+
const url = execSync("git config --get remote.origin.url").toString().trim();
|
|
55
|
+
return url;
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
52
60
|
function getMergeBaseCommitShaWithDepth(input) {
|
|
53
61
|
execSync(
|
|
54
62
|
`git fetch --update-head-ok --depth ${input.depth} origin ${input.head}:${input.head}`
|
|
@@ -99,19 +107,27 @@ function listParentCommits(input) {
|
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
// src/ci-environment/services/bitrise.ts
|
|
102
|
-
|
|
110
|
+
function getPrNumber(context) {
|
|
111
|
+
const { env } = context;
|
|
103
112
|
return env.BITRISE_PULL_REQUEST ? Number(env.BITRISE_PULL_REQUEST) : null;
|
|
104
|
-
}
|
|
113
|
+
}
|
|
114
|
+
function getRepository(context) {
|
|
115
|
+
const { env } = context;
|
|
116
|
+
if (env.BITRISEIO_GIT_REPOSITORY_OWNER && env.BITRISEIO_GIT_REPOSITORY_SLUG) {
|
|
117
|
+
return `${env.BITRISEIO_GIT_REPOSITORY_OWNER}/${env.BITRISEIO_GIT_REPOSITORY_SLUG}`;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
105
121
|
var service = {
|
|
106
122
|
name: "Bitrise",
|
|
107
123
|
key: "bitrise",
|
|
108
124
|
detect: ({ env }) => Boolean(env.BITRISE_IO),
|
|
109
|
-
config: (
|
|
125
|
+
config: (context) => {
|
|
126
|
+
const { env } = context;
|
|
110
127
|
return {
|
|
111
128
|
commit: env.BITRISE_GIT_COMMIT || null,
|
|
112
129
|
branch: env.BITRISE_GIT_BRANCH || null,
|
|
113
|
-
|
|
114
|
-
repository: env.BITRISEIO_GIT_REPOSITORY_SLUG || null,
|
|
130
|
+
repository: getRepository(context),
|
|
115
131
|
jobId: null,
|
|
116
132
|
runId: null,
|
|
117
133
|
runAttempt: null,
|
|
@@ -126,18 +142,40 @@ var service = {
|
|
|
126
142
|
};
|
|
127
143
|
var bitrise_default = service;
|
|
128
144
|
|
|
145
|
+
// src/util/url.ts
|
|
146
|
+
function getRepositoryNameFromURL(url) {
|
|
147
|
+
const sshMatch = url.match(/^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
148
|
+
if (sshMatch && sshMatch[1] && sshMatch[2]) {
|
|
149
|
+
return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
150
|
+
}
|
|
151
|
+
const httpsMatch = url.match(
|
|
152
|
+
/^(?:https?|git):\/\/[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/
|
|
153
|
+
);
|
|
154
|
+
if (httpsMatch && httpsMatch[1] && httpsMatch[2]) {
|
|
155
|
+
return `${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
129
160
|
// src/ci-environment/services/buildkite.ts
|
|
161
|
+
function getRepository2(context) {
|
|
162
|
+
const { env } = context;
|
|
163
|
+
if (env.BUILDKITE_REPO) {
|
|
164
|
+
return getRepositoryNameFromURL(env.BUILDKITE_REPO);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
130
168
|
var service2 = {
|
|
131
169
|
name: "Buildkite",
|
|
132
170
|
key: "buildkite",
|
|
133
171
|
detect: ({ env }) => Boolean(env.BUILDKITE),
|
|
134
|
-
config: (
|
|
172
|
+
config: (context) => {
|
|
173
|
+
const { env } = context;
|
|
135
174
|
return {
|
|
136
175
|
// Buildkite doesn't work well so we fallback to git to ensure we have commit and branch
|
|
137
176
|
commit: env.BUILDKITE_COMMIT || head() || null,
|
|
138
177
|
branch: env.BUILDKITE_BRANCH || branch() || null,
|
|
139
|
-
|
|
140
|
-
repository: env.BUILDKITE_PROJECT_SLUG || null,
|
|
178
|
+
repository: getRepository2(context),
|
|
141
179
|
jobId: null,
|
|
142
180
|
runId: null,
|
|
143
181
|
runAttempt: null,
|
|
@@ -256,11 +294,15 @@ function getBranchFromPayload(payload) {
|
|
|
256
294
|
}
|
|
257
295
|
return null;
|
|
258
296
|
}
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
297
|
+
function getRepository3(context, payload) {
|
|
298
|
+
const { env } = context;
|
|
299
|
+
if (payload && "pull_request" in payload && payload.pull_request) {
|
|
300
|
+
const pr = payload.pull_request;
|
|
301
|
+
if (pr.head && pr.head.repo && pr.head.repo.full_name) {
|
|
302
|
+
return pr.head.repo.full_name;
|
|
303
|
+
}
|
|
262
304
|
}
|
|
263
|
-
return env.GITHUB_REPOSITORY
|
|
305
|
+
return env.GITHUB_REPOSITORY || null;
|
|
264
306
|
}
|
|
265
307
|
function readEventPayload({ env }) {
|
|
266
308
|
if (!env.GITHUB_EVENT_PATH) {
|
|
@@ -297,8 +339,7 @@ var service4 = {
|
|
|
297
339
|
const pullRequest = payload ? getPullRequestFromPayload(payload) : await getPullRequestFromHeadSha(context, sha);
|
|
298
340
|
return {
|
|
299
341
|
commit: sha,
|
|
300
|
-
|
|
301
|
-
repository: getRepositoryFromContext(context),
|
|
342
|
+
repository: getRepository3(context, payload),
|
|
302
343
|
jobId: env.GITHUB_JOB || null,
|
|
303
344
|
runId: env.GITHUB_RUN_ID || null,
|
|
304
345
|
runAttempt: env.GITHUB_RUN_ATTEMPT ? Number(env.GITHUB_RUN_ATTEMPT) : null,
|
|
@@ -315,24 +356,34 @@ var service4 = {
|
|
|
315
356
|
var github_actions_default = service4;
|
|
316
357
|
|
|
317
358
|
// src/ci-environment/services/circleci.ts
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
const matches =
|
|
359
|
+
function getPrNumber2(context) {
|
|
360
|
+
const { env } = context;
|
|
361
|
+
const matches = /pull\/(\d+)/.exec(env.CIRCLE_PULL_REQUEST || "");
|
|
321
362
|
if (matches) {
|
|
322
363
|
return Number(matches[1]);
|
|
323
364
|
}
|
|
324
365
|
return null;
|
|
325
|
-
}
|
|
366
|
+
}
|
|
367
|
+
function getRepository4(context) {
|
|
368
|
+
const { env } = context;
|
|
369
|
+
if (env.CIRCLE_PR_REPONAME && env.CIRCLE_PR_USERNAME) {
|
|
370
|
+
return `${env.CIRCLE_PR_USERNAME}/${env.CIRCLE_PR_REPONAME}`;
|
|
371
|
+
}
|
|
372
|
+
if (env.CIRCLE_PROJECT_USERNAME && env.CIRCLE_PROJECT_REPONAME) {
|
|
373
|
+
return `${env.CIRCLE_PROJECT_USERNAME}/${env.CIRCLE_PROJECT_REPONAME}`;
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
326
377
|
var service5 = {
|
|
327
378
|
name: "CircleCI",
|
|
328
379
|
key: "circleci",
|
|
329
380
|
detect: ({ env }) => Boolean(env.CIRCLECI),
|
|
330
|
-
config: (
|
|
381
|
+
config: (context) => {
|
|
382
|
+
const { env } = context;
|
|
331
383
|
return {
|
|
332
384
|
commit: env.CIRCLE_SHA1 || null,
|
|
333
385
|
branch: env.CIRCLE_BRANCH || null,
|
|
334
|
-
|
|
335
|
-
repository: env.CIRCLE_PROJECT_REPONAME || null,
|
|
386
|
+
repository: getRepository4(context),
|
|
336
387
|
jobId: null,
|
|
337
388
|
runId: null,
|
|
338
389
|
runAttempt: null,
|
|
@@ -348,24 +399,23 @@ var service5 = {
|
|
|
348
399
|
var circleci_default = service5;
|
|
349
400
|
|
|
350
401
|
// src/ci-environment/services/travis.ts
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
402
|
+
function getRepository5(context) {
|
|
403
|
+
const { env } = context;
|
|
404
|
+
if (env.TRAVIS_PULL_REQUEST_SLUG) {
|
|
405
|
+
return env.TRAVIS_PULL_REQUEST_SLUG;
|
|
354
406
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
var getRepository = ({ env }) => {
|
|
358
|
-
if (!env.TRAVIS_REPO_SLUG) {
|
|
359
|
-
return null;
|
|
407
|
+
if (env.TRAVIS_REPO_SLUG) {
|
|
408
|
+
return env.TRAVIS_REPO_SLUG;
|
|
360
409
|
}
|
|
361
|
-
return
|
|
362
|
-
}
|
|
363
|
-
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
function getPrNumber3(context) {
|
|
413
|
+
const { env } = context;
|
|
364
414
|
if (env.TRAVIS_PULL_REQUEST) {
|
|
365
415
|
return Number(env.TRAVIS_PULL_REQUEST);
|
|
366
416
|
}
|
|
367
417
|
return null;
|
|
368
|
-
}
|
|
418
|
+
}
|
|
369
419
|
var service6 = {
|
|
370
420
|
name: "Travis CI",
|
|
371
421
|
key: "travis",
|
|
@@ -375,8 +425,7 @@ var service6 = {
|
|
|
375
425
|
return {
|
|
376
426
|
commit: env.TRAVIS_COMMIT || null,
|
|
377
427
|
branch: env.TRAVIS_BRANCH || null,
|
|
378
|
-
|
|
379
|
-
repository: getRepository(ctx),
|
|
428
|
+
repository: getRepository5(ctx),
|
|
380
429
|
jobId: null,
|
|
381
430
|
runId: null,
|
|
382
431
|
runAttempt: null,
|
|
@@ -392,16 +441,23 @@ var service6 = {
|
|
|
392
441
|
var travis_default = service6;
|
|
393
442
|
|
|
394
443
|
// src/ci-environment/services/gitlab.ts
|
|
444
|
+
function getRepository6(context) {
|
|
445
|
+
const { env } = context;
|
|
446
|
+
if (env.CI_MERGE_REQUEST_PROJECT_PATH) {
|
|
447
|
+
return env.CI_MERGE_REQUEST_PROJECT_PATH;
|
|
448
|
+
}
|
|
449
|
+
return env.CI_PROJECT_PATH || null;
|
|
450
|
+
}
|
|
395
451
|
var service7 = {
|
|
396
452
|
name: "GitLab",
|
|
397
453
|
key: "gitlab",
|
|
398
454
|
detect: ({ env }) => env.GITLAB_CI === "true",
|
|
399
|
-
config: (
|
|
455
|
+
config: (context) => {
|
|
456
|
+
const { env } = context;
|
|
400
457
|
return {
|
|
401
458
|
commit: env.CI_COMMIT_SHA || null,
|
|
402
459
|
branch: env.CI_COMMIT_REF_NAME || null,
|
|
403
|
-
|
|
404
|
-
repository: null,
|
|
460
|
+
repository: getRepository6(context),
|
|
405
461
|
jobId: null,
|
|
406
462
|
runId: null,
|
|
407
463
|
runAttempt: null,
|
|
@@ -417,6 +473,13 @@ var service7 = {
|
|
|
417
473
|
var gitlab_default = service7;
|
|
418
474
|
|
|
419
475
|
// src/ci-environment/services/git.ts
|
|
476
|
+
function getRepository7() {
|
|
477
|
+
const repositoryURL = getRepositoryURL();
|
|
478
|
+
if (!repositoryURL) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
return getRepositoryNameFromURL(repositoryURL);
|
|
482
|
+
}
|
|
420
483
|
var service8 = {
|
|
421
484
|
name: "Git",
|
|
422
485
|
key: "git",
|
|
@@ -425,8 +488,7 @@ var service8 = {
|
|
|
425
488
|
return {
|
|
426
489
|
commit: head() || null,
|
|
427
490
|
branch: branch() || null,
|
|
428
|
-
|
|
429
|
-
repository: null,
|
|
491
|
+
repository: getRepository7(),
|
|
430
492
|
jobId: null,
|
|
431
493
|
runId: null,
|
|
432
494
|
runAttempt: null,
|
|
@@ -673,7 +735,6 @@ async function readConfig(options = {}) {
|
|
|
673
735
|
prBaseBranch: config.get("prBaseBranch") || ciEnv?.prBaseBranch || null,
|
|
674
736
|
referenceBranch: options.referenceBranch || config.get("referenceBranch") || null,
|
|
675
737
|
referenceCommit: options.referenceCommit || config.get("referenceCommit") || null,
|
|
676
|
-
owner: ciEnv?.owner || null,
|
|
677
738
|
repository: ciEnv?.repository || null,
|
|
678
739
|
jobId: ciEnv?.jobId || null,
|
|
679
740
|
runId: ciEnv?.runId || null,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@argos-ci/core",
|
|
3
3
|
"description": "Node.js SDK for visual testing with Argos.",
|
|
4
|
-
"version": "3.2.
|
|
4
|
+
"version": "3.2.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"exports": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@argos-ci/api-client": "0.8.
|
|
44
|
-
"@argos-ci/util": "2.3.
|
|
43
|
+
"@argos-ci/api-client": "0.8.2",
|
|
44
|
+
"@argos-ci/util": "2.3.3",
|
|
45
45
|
"axios": "^1.8.4",
|
|
46
46
|
"convict": "^6.2.4",
|
|
47
47
|
"debug": "^4.4.0",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"@types/convict": "^6.1.6",
|
|
55
55
|
"@types/debug": "^4.1.12",
|
|
56
56
|
"@types/tmp": "^0.2.6",
|
|
57
|
-
"msw": "^2.7.3"
|
|
57
|
+
"msw": "^2.7.3",
|
|
58
|
+
"vitest": "catalog:"
|
|
58
59
|
},
|
|
59
60
|
"scripts": {
|
|
60
61
|
"build": "tsup && cp ./src/index.cjs ./dist",
|
|
@@ -64,5 +65,5 @@
|
|
|
64
65
|
"lint": "eslint .",
|
|
65
66
|
"test": "vitest"
|
|
66
67
|
},
|
|
67
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "c54dac3320a10630c31549153473b70a2b42c2b6"
|
|
68
69
|
}
|