summaryrefslogtreecommitdiff
path: root/packages/taler-util/src
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-05 10:30:19 -0300
committerSebastian <sebasjm@gmail.com>2024-04-05 10:30:34 -0300
commit70151490bd79d38f8064b720fc124d1774a18235 (patch)
tree3d3238bf02743d38b243581457130804939f0473 /packages/taler-util/src
parent493fe38a603cc7a54a9d36b235da7abad3a10a92 (diff)
downloadwallet-core-70151490bd79d38f8064b720fc124d1774a18235.tar.gz
wallet-core-70151490bd79d38f8064b720fc124d1774a18235.tar.bz2
wallet-core-70151490bd79d38f8064b720fc124d1774a18235.zip
fix listing, add cache invalidation, fix codec for accounts
Diffstat (limited to 'packages/taler-util/src')
-rw-r--r--packages/taler-util/src/http-client/merchant.ts173
-rw-r--r--packages/taler-util/src/http-client/types.ts4
-rw-r--r--packages/taler-util/src/time.ts6
3 files changed, 141 insertions, 42 deletions
diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts
index cfe3155d1..23d7c76df 100644
--- a/packages/taler-util/src/http-client/merchant.ts
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -81,9 +81,36 @@ export type TalerMerchantInstanceErrorsByMethod<
export enum TalerMerchantInstanceCacheEviction {
CREATE_ORDER,
+ UPDATE_ORDER,
+ DELETE_ORDER,
+ UPDATE_CURRENT_INSTANCE,
+ DELETE_CURRENT_INSTANCE,
+ CREATE_BANK_ACCOUNT,
+ UPDATE_BANK_ACCOUNT,
+ DELETE_BANK_ACCOUNT,
+ CREATE_PRODUCT,
+ UPDATE_PRODUCT,
+ DELETE_PRODUCT,
+ CREATE_TRANSFER,
+ DELETE_TRANSFER,
+ CREATE_DEVICE,
+ UPDATE_DEVICE,
+ DELETE_DEVICE,
+ CREATE_TEMPLATE,
+ UPDATE_TEMPLATE,
+ DELETE_TEMPLATE,
+ CREATE_WEBHOOK,
+ UPDATE_WEBHOOK,
+ DELETE_WEBHOOK,
+ CREATE_TOKENFAMILY,
+ UPDATE_TOKENFAMILY,
+ DELETE_TOKENFAMILY,
+ LAST,
}
export enum TalerMerchantManagementCacheEviction {
- CREATE_INSTANCE,
+ CREATE_INSTANCE = TalerMerchantInstanceCacheEviction.LAST + 1,
+ UPDATE_INSTANCE,
+ DELETE_INSTANCE,
}
/**
* Protocol version spoken with the core bank.
@@ -150,8 +177,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForClaimResponse());
+ }
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -173,8 +202,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForPaymentResponse());
+ }
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.PaymentRequired:
@@ -274,8 +305,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForPaidRefundStatusResponse());
+ }
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
@@ -302,8 +335,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForAbortResponse());
+ }
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
@@ -330,8 +365,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForWalletRefundResponse());
+ }
case HttpStatusCode.BadRequest:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Forbidden:
@@ -399,8 +436,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_CURRENT_INSTANCE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -461,8 +500,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_CURRENT_INSTANCE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -550,8 +591,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_BANK_ACCOUNT);
return opSuccessFromHttp(resp, codecForAccountAddResponse());
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -583,8 +626,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_BANK_ACCOUNT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -669,8 +714,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_BANK_ACCOUNT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -704,8 +751,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_PRODUCT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound: // FIXME: missing in docs
@@ -738,8 +787,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -831,8 +882,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_PRODUCT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -860,8 +913,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_PRODUCT);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1049,8 +1104,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
@@ -1082,8 +1139,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_ORDER);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1120,8 +1179,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_ORDER);
return opSuccessFromHttp(resp, codecForMerchantRefundResponse());
+ }
case HttpStatusCode.Forbidden:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
@@ -1161,8 +1222,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TRANSFER);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1234,8 +1297,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TRANSFER);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1271,8 +1336,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_DEVICE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1302,8 +1369,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_DEVICE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1396,8 +1465,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_DEVICE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1430,8 +1501,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TEMPLATE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1461,8 +1534,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_TEMPLATE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1544,8 +1619,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TEMPLATE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1615,8 +1692,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_WEBHOOK);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1647,8 +1726,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_WEBHOOK);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1731,8 +1812,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_WEBHOOK);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1766,8 +1849,10 @@ export class TalerMerchantInstanceHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.CREATE_TOKENFAMILY);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1797,8 +1882,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY);
return opSuccessFromHttp(resp, codecForTokenFamilyDetails());
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1883,8 +1970,10 @@ export class TalerMerchantInstanceHttpClient {
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(TalerMerchantInstanceCacheEviction.DELETE_TOKENFAMILY);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -1913,15 +2002,15 @@ export type TalerMerchantManagementErrorsByMethod<
> = FailCasesByMethod<TalerMerchantManagementHttpClient, prop>;
export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttpClient {
- readonly cacheManagementEvictor: CacheEvictor<TalerMerchantManagementCacheEviction>;
+ readonly cacheManagementEvictor: CacheEvictor<TalerMerchantInstanceCacheEviction | TalerMerchantManagementCacheEviction>;
constructor(
readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
- cacheManagementEvictor?: CacheEvictor<TalerMerchantManagementCacheEviction>,
- cacheEvictor?: CacheEvictor<TalerMerchantInstanceCacheEviction>,
+ // cacheManagementEvictor?: CacheEvictor<TalerMerchantManagementCacheEviction>,
+ cacheEvictor?: CacheEvictor<TalerMerchantInstanceCacheEviction | TalerMerchantManagementCacheEviction>,
) {
super(baseUrl, httpClient, cacheEvictor);
- this.cacheManagementEvictor = cacheManagementEvictor ?? nullEvictor;
+ this.cacheManagementEvictor = cacheEvictor ?? nullEvictor;
}
getSubInstanceAPI(instanceId: string) {
@@ -1953,9 +2042,7 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp
switch (resp.status) {
case HttpStatusCode.NoContent: {
- this.cacheManagementEvictor.notifySuccess(
- TalerMerchantManagementCacheEviction.CREATE_INSTANCE,
- );
+ this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.CREATE_INSTANCE);
return opEmptySuccess(resp);
}
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
@@ -2022,8 +2109,10 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.UPDATE_INSTANCE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
@@ -2112,8 +2201,10 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp
headers,
});
switch (resp.status) {
- case HttpStatusCode.NoContent:
+ case HttpStatusCode.NoContent: {
+ this.cacheManagementEvictor.notifySuccess(TalerMerchantManagementCacheEviction.DELETE_INSTANCE);
return opEmptySuccess(resp);
+ }
case HttpStatusCode.Unauthorized: // FIXME: missing in docs
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.NotFound:
diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts
index dd2161deb..ea7ba341b 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -777,7 +777,7 @@ export const codecForTransferDetails =
.property("payto_uri", codecForPaytoString())
.property("exchange_url", codecForURL())
.property("transfer_serial_id", codecForNumber())
- .property("execution_time", codecForTimestamp)
+ .property("execution_time", codecOptional(codecForTimestamp))
.property("verified", codecOptional(codecForBoolean()))
.property("confirmed", codecOptional(codecForBoolean()))
.build("TalerMerchantApi.TransferDetails");
@@ -3781,6 +3781,8 @@ export namespace TalerMerchantApi {
// List of accounts that are known for the instance.
accounts: BankAccountSummaryEntry[];
}
+
+ // TODO: missing in docs
export interface BankAccountSummaryEntry {
// payto:// URI of the account.
payto_uri: PaytoString;
diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 2e24856ee..95b4911a0 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -604,6 +604,9 @@ export function durationAdd(d1: Duration, d2: Duration): Duration {
export const codecForAbsoluteTime: Codec<AbsoluteTime> = {
decode(x: any, c?: Context): AbsoluteTime {
+ if (x === undefined) {
+ throw Error(`got undefined and expected absolute time at ${renderContext(c)}`);
+ }
const t_ms = x.t_ms;
if (typeof t_ms === "string") {
if (t_ms === "never") {
@@ -619,6 +622,9 @@ export const codecForAbsoluteTime: Codec<AbsoluteTime> = {
export const codecForTimestamp: Codec<TalerProtocolTimestamp> = {
decode(x: any, c?: Context): TalerProtocolTimestamp {
// Compatibility, should be removed soon.
+ if (x === undefined) {
+ throw Error(`got undefined and expected timestamp at ${renderContext(c)}`);
+ }
const t_ms = x.t_ms;
if (typeof t_ms === "string") {
if (t_ms === "never") {