@client-compose/client 0.9.0 → 0.10.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/strings.d.ts +2 -15
- package/dist/strings.d.ts.map +1 -1
- package/dist/strings.js +3 -76
- package/dist/strings.js.map +1 -1
- package/package.json +3 -3
- package/src/strings.ts +8 -105
package/dist/strings.d.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import { type AppStringsDescriptor, type ClientContext, type
|
|
2
|
-
|
|
3
|
-
export declare function pluralCategory(rules: PluralRules | undefined, count: number): string;
|
|
4
|
-
/** The values a string's params take. A counted string's `count` picks its variant. */
|
|
5
|
-
export type StringValues = Record<string, string | number>;
|
|
6
|
-
/**
|
|
7
|
-
* A string's words with its params filled in. Variants are chosen by the exact count, then its
|
|
8
|
-
* plural category, then `#*`.
|
|
9
|
-
*/
|
|
10
|
-
export declare function formatString(template: StringTemplate | undefined, values?: StringValues, plurals?: PluralRules): string;
|
|
11
|
-
/**
|
|
12
|
-
* Strings as a renderer receives them: each declared string with no params as its text, each with
|
|
13
|
-
* params as a function filling them in. A string the composer didn't send is empty.
|
|
14
|
-
*/
|
|
15
|
-
export declare function buildStrings<S>(declared: StringParams, strings: Strings | undefined, plurals: PluralRules | undefined): S;
|
|
1
|
+
import { type AppStringsDescriptor, type ClientContext, type StringsResponse } from "@client-compose/protocol";
|
|
2
|
+
export { buildStrings, formatString, pluralCategory, type StringValues, } from "@client-compose/protocol";
|
|
16
3
|
/** The app strings, typed by their generated descriptor: `appStrings(AppStrings, response).retry`. */
|
|
17
4
|
export declare function appStrings<S>(descriptor: AppStringsDescriptor<S>, response: StringsResponse): S;
|
|
18
5
|
/** Thrown for a strings request the composer didn't answer with 200 and its strings. */
|
package/dist/strings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,oBAAoB,EAEzB,KAAK,aAAa,EAElB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAIlC,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,eAAe,GAAG,CAAC,CAE/F;AAED,wFAAwF;AACxF,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAKzC;AAYD,kEAAkE;AAClE,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,WAAgB,EACtB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,eAAe,CAAC,CAyB1B"}
|
package/dist/strings.js
CHANGED
|
@@ -1,82 +1,9 @@
|
|
|
1
1
|
// Strings (spec §4.7): the words a client shows, from the composer. Templates are filled in here,
|
|
2
2
|
// counted strings pick their variant by the language's plural rules, and an island's strings
|
|
3
3
|
// become the typed `t` its renderer receives.
|
|
4
|
-
import { PROTOCOL_VERSION, } from "@client-compose/protocol";
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
export function pluralCategory(rules, count) {
|
|
8
|
-
for (const [category, condition] of Object.entries(rules ?? {})) {
|
|
9
|
-
if (condition !== undefined && holds(condition, count)) {
|
|
10
|
-
return category;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return "other";
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Whether a CLDR plural condition holds for a whole number: `or` of `and`s of relations like
|
|
17
|
-
* `i % 10 = 2..4` or `v = 0`. For a whole number, `n` and `i` are the number, and `v`, `w`, `f`,
|
|
18
|
-
* `t`, `c` and `e` are zero.
|
|
19
|
-
*/
|
|
20
|
-
function holds(condition, count) {
|
|
21
|
-
return condition.split(" or ").some((all) => all.split(" and ").every((r) => relates(r, count)));
|
|
22
|
-
}
|
|
23
|
-
const RELATION = /^\s*([nivwftce])\s*(?:%\s*(\d+))?\s*(!=|=)\s*(.+?)\s*$/;
|
|
24
|
-
function relates(relation, count) {
|
|
25
|
-
const match = RELATION.exec(relation);
|
|
26
|
-
if (!match) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
const [, operand = "", modulus, op, ranges = ""] = match;
|
|
30
|
-
const base = operand === "n" || operand === "i" ? Math.abs(Math.trunc(count)) : 0;
|
|
31
|
-
const value = modulus === undefined ? base : base % Number(modulus);
|
|
32
|
-
const inRanges = ranges.split(",").some((range) => {
|
|
33
|
-
const [from = "", to] = range.trim().split("..");
|
|
34
|
-
return to === undefined ? value === Number(from) : value >= Number(from) && value <= Number(to);
|
|
35
|
-
});
|
|
36
|
-
return op === "=" ? inRanges : !inRanges;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* A string's words with its params filled in. Variants are chosen by the exact count, then its
|
|
40
|
-
* plural category, then `#*`.
|
|
41
|
-
*/
|
|
42
|
-
export function formatString(template, values = {}, plurals) {
|
|
43
|
-
if (template === undefined) {
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
|
-
const text = typeof template === "string" ? template : variant(template, values.count, plurals);
|
|
47
|
-
return text.replace(/\{([A-Za-z0-9]+)\}/g, (slot, name) => name in values ? String(values[name]) : slot);
|
|
48
|
-
}
|
|
49
|
-
function variant(variants, count, plurals) {
|
|
50
|
-
const n = Number(count);
|
|
51
|
-
if (Number.isFinite(n)) {
|
|
52
|
-
const exact = variants[`#${n}`];
|
|
53
|
-
if (exact !== undefined) {
|
|
54
|
-
return exact;
|
|
55
|
-
}
|
|
56
|
-
const category = variants[`#${pluralCategory(plurals, n)}`];
|
|
57
|
-
if (category !== undefined) {
|
|
58
|
-
return category;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return variants["#*"] ?? "";
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Strings as a renderer receives them: each declared string with no params as its text, each with
|
|
65
|
-
* params as a function filling them in. A string the composer didn't send is empty.
|
|
66
|
-
*/
|
|
67
|
-
export function buildStrings(declared, strings, plurals) {
|
|
68
|
-
const t = {};
|
|
69
|
-
for (const [key, params] of Object.entries(declared)) {
|
|
70
|
-
const template = strings?.[key];
|
|
71
|
-
t[key] =
|
|
72
|
-
params.length === 0
|
|
73
|
-
? formatString(template, {}, plurals)
|
|
74
|
-
: (values) => formatString(template, values, plurals);
|
|
75
|
-
}
|
|
76
|
-
// The declared keys and params are the ones codegen typed S with.
|
|
77
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
78
|
-
return t;
|
|
79
|
-
}
|
|
4
|
+
import { buildStrings, PROTOCOL_VERSION, } from "@client-compose/protocol";
|
|
5
|
+
// Shared with composers; re-exported so apps need only this package.
|
|
6
|
+
export { buildStrings, formatString, pluralCategory, } from "@client-compose/protocol";
|
|
80
7
|
// ---- App strings ----
|
|
81
8
|
/** The app strings, typed by their generated descriptor: `appStrings(AppStrings, response).retry`. */
|
|
82
9
|
export function appStrings(descriptor, response) {
|
package/dist/strings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.js","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,6FAA6F;AAC7F,8CAA8C;AAE9C,OAAO,
|
|
1
|
+
{"version":3,"file":"strings.js","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,6FAA6F;AAC7F,8CAA8C;AAE9C,OAAO,EAEL,YAAY,EAEZ,gBAAgB,GAEjB,MAAM,0BAA0B,CAAC;AAElC,qEAAqE;AACrE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,GAEf,MAAM,0BAA0B,CAAC;AAElC,wBAAwB;AAExB,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAI,UAAmC,EAAE,QAAyB;IAC1F,OAAO,YAAY,CAAI,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjF,CAAC;AAED,wFAAwF;AACxF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAS;IAEtB,YAAY,MAAc,EAAE,IAAY;QACtC,KAAK,CAAC,sCAAsC,MAAM,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,MAAqB,EACrB,OAAoB,EAAE,EACtB,YAA0B,KAAK;IAE/B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,UAAU,EAAE;QAChD,GAAG,IAAI;QACP,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC;KAC7D,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@client-compose/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Framework-agnostic Client Compose client: island registry, screen requests and render planning.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"client-compose",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"LICENSE"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@client-compose/protocol": "0.
|
|
47
|
+
"@client-compose/protocol": "0.7.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@client-compose/conformance": "0.
|
|
50
|
+
"@client-compose/conformance": "0.10.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/strings.ts
CHANGED
|
@@ -4,116 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
type AppStringsDescriptor,
|
|
7
|
+
buildStrings,
|
|
7
8
|
type ClientContext,
|
|
8
|
-
type PluralRules,
|
|
9
9
|
PROTOCOL_VERSION,
|
|
10
|
-
type StringParams,
|
|
11
|
-
type Strings,
|
|
12
10
|
type StringsResponse,
|
|
13
|
-
type StringTemplate,
|
|
14
11
|
} from "@client-compose/protocol";
|
|
15
12
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return "other";
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Whether a CLDR plural condition holds for a whole number: `or` of `and`s of relations like
|
|
30
|
-
* `i % 10 = 2..4` or `v = 0`. For a whole number, `n` and `i` are the number, and `v`, `w`, `f`,
|
|
31
|
-
* `t`, `c` and `e` are zero.
|
|
32
|
-
*/
|
|
33
|
-
function holds(condition: string, count: number): boolean {
|
|
34
|
-
return condition.split(" or ").some((all) => all.split(" and ").every((r) => relates(r, count)));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const RELATION = /^\s*([nivwftce])\s*(?:%\s*(\d+))?\s*(!=|=)\s*(.+?)\s*$/;
|
|
38
|
-
|
|
39
|
-
function relates(relation: string, count: number): boolean {
|
|
40
|
-
const match = RELATION.exec(relation);
|
|
41
|
-
if (!match) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
const [, operand = "", modulus, op, ranges = ""] = match;
|
|
45
|
-
const base = operand === "n" || operand === "i" ? Math.abs(Math.trunc(count)) : 0;
|
|
46
|
-
const value = modulus === undefined ? base : base % Number(modulus);
|
|
47
|
-
const inRanges = ranges.split(",").some((range) => {
|
|
48
|
-
const [from = "", to] = range.trim().split("..");
|
|
49
|
-
return to === undefined ? value === Number(from) : value >= Number(from) && value <= Number(to);
|
|
50
|
-
});
|
|
51
|
-
return op === "=" ? inRanges : !inRanges;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ---- Templates ----
|
|
55
|
-
|
|
56
|
-
/** The values a string's params take. A counted string's `count` picks its variant. */
|
|
57
|
-
export type StringValues = Record<string, string | number>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* A string's words with its params filled in. Variants are chosen by the exact count, then its
|
|
61
|
-
* plural category, then `#*`.
|
|
62
|
-
*/
|
|
63
|
-
export function formatString(
|
|
64
|
-
template: StringTemplate | undefined,
|
|
65
|
-
values: StringValues = {},
|
|
66
|
-
plurals?: PluralRules,
|
|
67
|
-
): string {
|
|
68
|
-
if (template === undefined) {
|
|
69
|
-
return "";
|
|
70
|
-
}
|
|
71
|
-
const text = typeof template === "string" ? template : variant(template, values.count, plurals);
|
|
72
|
-
return text.replace(/\{([A-Za-z0-9]+)\}/g, (slot, name: string) =>
|
|
73
|
-
name in values ? String(values[name]) : slot,
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function variant(
|
|
78
|
-
variants: Record<string, string>,
|
|
79
|
-
count: string | number | undefined,
|
|
80
|
-
plurals: PluralRules | undefined,
|
|
81
|
-
): string {
|
|
82
|
-
const n = Number(count);
|
|
83
|
-
if (Number.isFinite(n)) {
|
|
84
|
-
const exact = variants[`#${n}`];
|
|
85
|
-
if (exact !== undefined) {
|
|
86
|
-
return exact;
|
|
87
|
-
}
|
|
88
|
-
const category = variants[`#${pluralCategory(plurals, n)}`];
|
|
89
|
-
if (category !== undefined) {
|
|
90
|
-
return category;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return variants["#*"] ?? "";
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Strings as a renderer receives them: each declared string with no params as its text, each with
|
|
98
|
-
* params as a function filling them in. A string the composer didn't send is empty.
|
|
99
|
-
*/
|
|
100
|
-
export function buildStrings<S>(
|
|
101
|
-
declared: StringParams,
|
|
102
|
-
strings: Strings | undefined,
|
|
103
|
-
plurals: PluralRules | undefined,
|
|
104
|
-
): S {
|
|
105
|
-
const t: Record<string, string | ((values: StringValues) => string)> = {};
|
|
106
|
-
for (const [key, params] of Object.entries(declared)) {
|
|
107
|
-
const template = strings?.[key];
|
|
108
|
-
t[key] =
|
|
109
|
-
params.length === 0
|
|
110
|
-
? formatString(template, {}, plurals)
|
|
111
|
-
: (values: StringValues) => formatString(template, values, plurals);
|
|
112
|
-
}
|
|
113
|
-
// The declared keys and params are the ones codegen typed S with.
|
|
114
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
115
|
-
return t as S;
|
|
116
|
-
}
|
|
13
|
+
// Shared with composers; re-exported so apps need only this package.
|
|
14
|
+
export {
|
|
15
|
+
buildStrings,
|
|
16
|
+
formatString,
|
|
17
|
+
pluralCategory,
|
|
18
|
+
type StringValues,
|
|
19
|
+
} from "@client-compose/protocol";
|
|
117
20
|
|
|
118
21
|
// ---- App strings ----
|
|
119
22
|
|