Commit 9d2c6366 by Vladislav Lagunov

Добавлен алиасы модулей gettext persistent

parent 6894e538
import * as jed from 'jed';
import memoize from '../functions/memoize';
/** translations */
export interface Translations {
locale_data: { messages: Record<string, any> };
domain: string;
debug?: boolean;
}
/** application context with translations */
export interface Ctx {
translations: Translations|null;
}
/** For convenience also allow passing `Ctx` instead of just translations */
export type CtxOrMaybeTranslations = Ctx|Translations|null;
/** lazy string for deferred translation */
export type I18nString = (t: CtxOrMaybeTranslations) => string;
export type Gettext = (a: string) => string;
/** minimal sptrintf */
export function sprintf(format: string, ...args: any[]): string {
var i = 0;
return format.replace(/%(s|d)/g, function() {
return args[i++];
});
}
export function gettext(t: CtxOrMaybeTranslations, key: string): string {
return getJedIntance(resolveTranslations(t)).gettext(key);
}
export function pgettext(t: CtxOrMaybeTranslations, context: string, key: string): string {
return getJedIntance(resolveTranslations(t)).pgettext(context, key);
}
export function dcgettext(t: CtxOrMaybeTranslations, domain: string, key: string, context: string): string {
return getJedIntance(resolveTranslations(t)).dcgettext(domain, key, context);
}
export function dcnpgettext(t: CtxOrMaybeTranslations, domain: string, context: string, single: string, plural: string, n: number): string {
return getJedIntance(resolveTranslations(t)).dcnpgettext(domain, context, single, plural, n);
}
/** defered versions of functions */
export namespace deferred {
export function gettext(key: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).gettext(key);
}
export function pgettext(context: string, key: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).pgettext(context, key);
}
export function dcgettext(domain: string, key: string, context: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).dcgettext(domain, key, context);
}
export function dcnpgettext(domain: string, context: string, single: I18nString, plural: I18nString, n: number): I18nString {
return t => getJedIntance(resolveTranslations(t)).dcnpgettext(domain, context, single(t), plural(t), n);
}
export function sprintf(format: I18nString, ...args: any[]): I18nString {
return translations => {
const formatString = typeof(format) === 'string' ? format : format(translations);
var i = 0;
return formatString.replace(/%(s|d)/g, function() { return args[i++]; });
}
}
}
function resolveTranslations(t: CtxOrMaybeTranslations): Translations {
return !t ? defaultTranslations : t.hasOwnProperty('translations') ? (t['translations'] || defaultTranslations) : t as Translations;
}
/** memoized `Jed` constructor */
const getJedIntance = memoize((translations: Translations) => new jed.Jed(translations));
/** minimal empty translations jed needs */
export const defaultTranslations: Translations = { "locale_data" : { "messages" : { "" : { "domain": "messages", "lang": "en", "plural_forms" : "nplurals=2; plural=(n != 1);" } } }, "domain" : "messages", "debug" : false }
import { Eff, eff, Either, Decoder, success, failure, decode as t } from '../core';
import * as aesjs from 'aes-js';
/** options for `Cache` */
export interface CacheOptions {
key?: number[] | Uint8Array;
version?: string;
lifetime?: number;
expires?: number;
}
/** possible errors */
export type Err =
| { tag: 'NoItem' }
| { tag: 'SizeLimitExeeded' }
| { tag: 'InvalidPayload', problem: t.Problem }
| { tag: 'VersionMismatch', actual: string|null, expected: string }
/** persistent cache */
export class Cache<A> {
readonly _A: A;
constructor(
public name: string, // should be unique
public decoder: Decoder<A>,
public options: CacheOptions = {},
) {}
saveSync(payload: A): Either<Err,null> {
try {
const updatedAt = Date.now();
const createdAt = Date.now();
const version = this.options.version || null;
let stringContent = JSON.stringify({ updatedAt, createdAt, version, payload });
if (this.options.key) {
const enc = new aesjs.ModeOfOperation.ctr(this.options.key);
const bytes = aesjs.utils.utf8.toBytes(stringContent);
stringContent = aesjs.utils.hex.fromBytes(enc.encrypt(bytes));
}
localStorage.setItem(this.name, stringContent);
} catch(e) {
return failure({ tag: 'SizeLimitExeeded' } as Err);
}
return success(null);
}
save(payload: A): Eff<Err,null> {
return eff.lazy(() => this.saveSync(payload));
}
readSync(): Either<Err,A> {
let stringContent = localStorage.getItem(this.name);
if (this.options.key && stringContent !== null) {
const enc = new aesjs.ModeOfOperation.ctr(this.options.key);
const bytes = aesjs.utils.hex.toBytes(stringContent)
stringContent = aesjs.utils.utf8.fromBytes(enc.decrypt(bytes));
}
if (stringContent === null) return failure({ tag: 'NoItem' } as Err);
try {
const json = JSON.parse(stringContent);
// @ts-ignore
return cacheItemDecoder<A>(this.decoder).validate(json)
.mapLeft(problem => ({ tag: 'InvalidPayload', problem } as Err))
.chain(({payload, version}) => {
if (this.options.version && version !== version) {
return failure<Err>({ tag: 'VersionMismatch', expected: this.options.version, actual: version })
}
return success(payload);
});
} catch (e) {
return failure({ tag: 'InvalidPayload', problem: e.message } as Err);
}
}
read(): Eff<Err,A> {
return eff.lazy(() => this.readSync());
}
clearSync(): void {
localStorage.removeItem(this.name);
}
clear(): Eff<never, null> {
return eff.lazy(() => (this.clearSync(), success(null)));
}
modifySync(f: (a: A) => A): Either<Err, null> {
const orignal = this.readSync();
if (orignal.tag === 'Left') return orignal as any;
return this.saveSync(f(orignal.value));
}
modify(f: (a: A) => A): Eff<Err, null> {
return eff.lazy(() => this.modifySync(f));
}
}
// Data stored in storage
export interface CacheItem<A> {
createdAt: number;
updatedAt: number;
version: string|null;
payload: A;
}
// Decode a `CacheItem`
const cacheItemDecoder = <A>(decoder: Decoder<A>) => t.record({
createdAt: t.float,
updatedAt: t.float,
version: t.oneOf(t.string, t.null),
payload: decoder,
});
import * as jed from 'jed'; export * from '../gettext';
import { memoize } from 'lodash';
/** translations */
export interface Translations {
locale_data: { messages: Record<string, any> };
domain: string;
debug?: boolean;
}
/** application context with translations */
export interface Ctx {
translations: Translations|null;
}
/** For convenience also allow passing `Ctx` instead of just translations */
export type CtxOrMaybeTranslations = Ctx|Translations|null;
/** lazy string for deferred translation */
export type I18nString = (t: CtxOrMaybeTranslations) => string;
export type Gettext = (a: string) => string;
/** minimal sptrintf */
export function sprintf(format: string, ...args: any[]): string {
var i = 0;
return format.replace(/%(s|d)/g, function() {
return args[i++];
});
}
export function gettext(t: CtxOrMaybeTranslations, key: string): string {
return getJedIntance(resolveTranslations(t)).gettext(key);
}
export function pgettext(t: CtxOrMaybeTranslations, context: string, key: string): string {
return getJedIntance(resolveTranslations(t)).pgettext(context, key);
}
export function dcgettext(t: CtxOrMaybeTranslations, domain: string, key: string, context: string): string {
return getJedIntance(resolveTranslations(t)).dcgettext(domain, key, context);
}
export function dcnpgettext(t: CtxOrMaybeTranslations, domain: string, context: string, single: string, plural: string, n: number): string {
return getJedIntance(resolveTranslations(t)).dcnpgettext(domain, context, single, plural, n);
}
/** defered versions of functions */
export namespace deferred {
export function gettext(key: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).gettext(key);
}
export function pgettext(context: string, key: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).pgettext(context, key);
}
export function dcgettext(domain: string, key: string, context: string): I18nString {
return t => getJedIntance(resolveTranslations(t)).dcgettext(domain, key, context);
}
export function dcnpgettext(domain: string, context: string, single: I18nString, plural: I18nString, n: number): I18nString {
return t => getJedIntance(resolveTranslations(t)).dcnpgettext(domain, context, single(t), plural(t), n);
}
export function sprintf(format: I18nString, ...args: any[]): I18nString {
return translations => {
const formatString = typeof(format) === 'string' ? format : format(translations);
var i = 0;
return formatString.replace(/%(s|d)/g, function() { return args[i++]; });
}
}
}
function resolveTranslations(t: CtxOrMaybeTranslations): Translations {
return !t ? defaultTranslations : t.hasOwnProperty('translations') ? (t['translations'] || defaultTranslations) : t as Translations;
}
/** memoized `Jed` constructor */
const getJedIntance = memoize((translations: Translations) => new jed.Jed(translations));
/** minimal empty translations jed needs */
export const defaultTranslations: Translations = { "locale_data" : { "messages" : { "" : { "domain": "messages", "lang": "en", "plural_forms" : "nplurals=2; plural=(n != 1);" } } }, "domain" : "messages", "debug" : false }
import { Eff, eff, Either, Decoder, success, failure, decode as t } from '../core'; export * from '../persistent';
import * as aesjs from 'aes-js'; ';
/** options for `Cache` */
export interface CacheOptions {
key?: number[] | Uint8Array;
version?: string;
lifetime?: number;
expires?: number;
}
/** possible errors */
export type Err =
| { tag: 'NoItem' }
| { tag: 'SizeLimitExeeded' }
| { tag: 'InvalidPayload', problem: t.Problem }
| { tag: 'VersionMismatch', actual: string|null, expected: string }
/** persistent cache */
export class Cache<A> {
readonly _A: A;
constructor(
public name: string, // should be unique
public decoder: Decoder<A>,
public options: CacheOptions = {},
) {}
saveSync(payload: A): Either<Err,null> {
try {
const updatedAt = Date.now();
const createdAt = Date.now();
const version = this.options.version || null;
let stringContent = JSON.stringify({ updatedAt, createdAt, version, payload });
if (this.options.key) {
const enc = new aesjs.ModeOfOperation.ctr(this.options.key);
const bytes = aesjs.utils.utf8.toBytes(stringContent);
stringContent = aesjs.utils.hex.fromBytes(enc.encrypt(bytes));
}
localStorage.setItem(this.name, stringContent);
} catch(e) {
return failure({ tag: 'SizeLimitExeeded' } as Err);
}
return success(null);
}
save(payload: A): Eff<Err,null> {
return eff.lazy(() => this.saveSync(payload));
}
readSync(): Either<Err,A> {
let stringContent = localStorage.getItem(this.name);
if (this.options.key && stringContent !== null) {
const enc = new aesjs.ModeOfOperation.ctr(this.options.key);
const bytes = aesjs.utils.hex.toBytes(stringContent)
stringContent = aesjs.utils.utf8.fromBytes(enc.decrypt(bytes));
}
if (stringContent === null) return failure({ tag: 'NoItem' } as Err);
try {
const json = JSON.parse(stringContent);
// @ts-ignore
return cacheItemDecoder<A>(this.decoder).validate(json)
.mapLeft(problem => ({ tag: 'InvalidPayload', problem } as Err))
.chain(({payload, version}) => {
if (this.options.version && version !== version) {
return failure<Err>({ tag: 'VersionMismatch', expected: this.options.version, actual: version })
}
return success(payload);
});
} catch (e) {
return failure({ tag: 'InvalidPayload', problem: e.message } as Err);
}
}
read(): Eff<Err,A> {
return eff.lazy(() => this.readSync());
}
clearSync(): void {
localStorage.removeItem(this.name);
}
clear(): Eff<never, null> {
return eff.lazy(() => (this.clearSync(), success(null)));
}
modifySync(f: (a: A) => A): Either<Err, null> {
const orignal = this.readSync();
if (orignal.tag === 'Left') return orignal as any;
return this.saveSync(f(orignal.value));
}
modify(f: (a: A) => A): Eff<Err, null> {
return eff.lazy(() => this.modifySync(f));
}
}
// Data stored in storage
export interface CacheItem<A> {
createdAt: number;
updatedAt: number;
version: string|null;
payload: A;
}
// Decode a `CacheItem`
const cacheItemDecoder = <A>(decoder: Decoder<A>) => t.record({
createdAt: t.float,
updatedAt: t.float,
version: t.oneOf(t.string, t.null),
payload: decoder,
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment