@pacem/pacem-foundation 1.0.0-abel

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.
@@ -0,0 +1,20 @@
1
+ // namespace Pacem {
2
+ export class Keys {
3
+ static #seed;
4
+ static uniqueId() {
5
+ let seed = Keys.#seed || Date.now();
6
+ return Keys.#seed = ++seed;
7
+ }
8
+ static uniqueCode() {
9
+ const newid = Keys.uniqueId();
10
+ return newid.toBase62();
11
+ }
12
+ static randomString(length) {
13
+ let retval = '';
14
+ do {
15
+ retval += (Math.random() * 62).toBase62();
16
+ } while (retval.length < length);
17
+ return retval;
18
+ }
19
+ }
20
+ // }
@@ -0,0 +1,133 @@
1
+ /// <reference path="geom.ts" />
2
+ ;
3
+ export class Matrix2D {
4
+ static isIdentity(m) {
5
+ return m.a === 1 && m.d === 1 && m.b === 0 && m.f === 0 && m.c === 0 && m.e === 0;
6
+ }
7
+ static get identity() {
8
+ return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
9
+ }
10
+ static copy(m) {
11
+ return { a: m.a, b: m.b, c: m.c, d: m.d, e: m.e, f: m.f };
12
+ }
13
+ static scale(m, sx, sy = sx) {
14
+ return { a: m.a * sx, b: m.b, c: m.c, d: m.d * sy, e: m.e, f: m.f };
15
+ }
16
+ static translate(m, t) {
17
+ return { a: m.a, b: m.b, c: m.c, d: m.d, e: m.e + t.x, f: m.f + t.y };
18
+ }
19
+ static rotate(m, rad) {
20
+ const cos = Math.cos(rad), sin = Math.sin(rad);
21
+ return Matrix2D.multiply(m, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 });
22
+ }
23
+ static multiply(m1, m2) {
24
+ if ("x" in m1 && "y" in m1) {
25
+ if (typeof m2 === 'number') {
26
+ return { x: m1.x * m2, y: m1.y * m2 };
27
+ }
28
+ else {
29
+ return { x: m1.x * m2.a + m1.y * m2.c + m2.e, y: m1.x * m2.b + m1.y * m2.d + m2.f };
30
+ }
31
+ }
32
+ if (typeof m2 === 'number') {
33
+ return {
34
+ a: m1.a * m2, b: m1.b * m2, c: m1.c * m2, d: m1.d * m2, e: m1.e * m2, f: m1.f * m2
35
+ };
36
+ }
37
+ return {
38
+ a: m1.a * m2.a + m1.b * m2.c,
39
+ b: m1.a * m2.b + m1.b * m2.d,
40
+ c: m1.c * m2.a + m1.d * m2.c,
41
+ d: m1.c * m2.b + m1.d * m2.d,
42
+ e: m1.e * m2.a + m1.f * m2.c + m2.e,
43
+ f: m1.e * m2.b + m1.f * m2.d + m2.f
44
+ };
45
+ }
46
+ static det(m) {
47
+ return m.a * m.d - m.c * m.b;
48
+ }
49
+ static invert(m) {
50
+ const det = this.det(m);
51
+ if (det === 0) {
52
+ // NaM(2D) ?
53
+ return null;
54
+ }
55
+ const invdet = 1 / det;
56
+ /*
57
+ * 1. swap a, d
58
+ * 2. swap c, b and change signs
59
+ * 3. e := -e*d + f*c
60
+ * 4. f := e*b - f*a
61
+ * 5. divide everything by [det]
62
+ */
63
+ return this.multiply({
64
+ a: m.d,
65
+ b: -m.b,
66
+ c: -m.c,
67
+ d: m.a,
68
+ e: -m.e * m.d + m.c * m.f,
69
+ f: m.e * m.b - m.a * m.f
70
+ }, invdet);
71
+ }
72
+ static toComponents(m, transformOrigin = { x: 0, y: 0 }) {
73
+ const scaleX = Math.sqrt(m.a * m.a + m.c * m.c);
74
+ const scaleY = Math.sqrt(m.b * m.b + m.d * m.d);
75
+ const cos = scaleX != 0 ? m.a / scaleX : 0;
76
+ const sin = scaleX != 0 ? m.c / scaleX : 0;
77
+ const rotation = Math.atan2(sin, cos);
78
+ const { x: px, y: py } = transformOrigin;
79
+ const translation = {
80
+ x: m.e - px * (scaleX * cos - 1) + py * scaleX * sin,
81
+ y: m.f - py * (scaleY * cos - 1) - px * scaleY * sin,
82
+ };
83
+ return {
84
+ scaleX, scaleY, rotation,
85
+ translation
86
+ };
87
+ }
88
+ /**
89
+ * Returns the matrix that transforms the triangle defined by the points p0, p1, p2 into the triangle defined by the points q0, q1, q2.
90
+ * @param param0 Triangle defined by the points p0, p1, p2 (before transformation).
91
+ * @param param1 Triangle defined by the points q0, q1, q2 (after transformation).
92
+ * @returns Transformation matrix.
93
+ */
94
+ static fromTransform([p0, p1, p2], [q0, q1, q2]) {
95
+ const det = p0.x * (p1.y - p2.y) -
96
+ p0.y * (p1.x - p2.x) +
97
+ (p1.x * p2.y - p2.x * p1.y);
98
+ if (Math.abs(det) < 1e-10) {
99
+ throw new Error("Points are collinear; matrix is singular and cannot be solved.");
100
+ }
101
+ const idet = 1 / det;
102
+ // Matrix inversion of the 3x3 augmented source points
103
+ // then multiplied by the destination points to get rows 1 and 2
104
+ return {
105
+ a: idet * (q0.x * (p1.y - p2.y) + q1.x * (p2.y - p0.y) + q2.x * (p0.y - p1.y)),
106
+ b: idet * (q0.y * (p1.y - p2.y) + q1.y * (p2.y - p0.y) + q2.y * (p0.y - p1.y)),
107
+ c: idet * (q0.x * (p2.x - p1.x) + q1.x * (p0.x - p2.x) + q2.x * (p1.x - p0.x)),
108
+ d: idet * (q0.y * (p2.x - p1.x) + q1.y * (p0.x - p2.x) + q2.y * (p1.x - p0.x)),
109
+ e: idet * (q0.x * (p1.x * p2.y - p2.x * p1.y) +
110
+ q1.x * (p2.x * p0.y - p0.x * p2.y) +
111
+ q2.x * (p0.x * p1.y - p1.x * p0.y)),
112
+ f: idet * (q0.y * (p1.x * p2.y - p2.x * p1.y) +
113
+ q1.y * (p2.x * p0.y - p0.x * p2.y) +
114
+ q2.y * (p0.x * p1.y - p1.x * p0.y))
115
+ };
116
+ }
117
+ /**
118
+ * Returns whether two matrices are equal to each other, within a certain precision (number of decimal digits).
119
+ * @param m1
120
+ * @param m2
121
+ * @param precision
122
+ * @returns
123
+ */
124
+ static areClose(m1, m2, precision = 12) {
125
+ return m1.a.isCloseTo(m2.a, precision)
126
+ && m1.b.isCloseTo(m2.b, precision)
127
+ && m1.c.isCloseTo(m2.c, precision)
128
+ && m1.d.isCloseTo(m2.d, precision)
129
+ && m1.e.isCloseTo(m2.e, precision)
130
+ && m1.f.isCloseTo(m2.f, precision);
131
+ }
132
+ }
133
+ // }
@@ -0,0 +1,23 @@
1
+ /// <reference path="dates.ts" />
2
+ import { Dates } from "./dates";
3
+ // namespace Pacem {
4
+ export class NullChecker {
5
+ static isNull(o) {
6
+ return o === null || o === void 0;
7
+ }
8
+ static isEmpty(o) {
9
+ try {
10
+ return (Array.isArray(o) && o.length === 0)
11
+ || (typeof o === 'string' && o === '')
12
+ || (typeof o === 'object' && Object.keys(o).length === 0 && !Dates.isDate(o) && !(o instanceof RegExp) && !(o instanceof FileSystemDirectoryHandle) && !(o instanceof FileSystemFileHandle));
13
+ }
14
+ catch (e) {
15
+ // Object.keys(null) would throw...
16
+ return false;
17
+ }
18
+ }
19
+ static isNullOrEmpty(o) {
20
+ return NullChecker.isNull(o) || NullChecker.isEmpty(o);
21
+ }
22
+ }
23
+ //}
@@ -0,0 +1,33 @@
1
+ Number.prototype.isCloseTo = function (other, precision = 14) {
2
+ const delta = Number(`1e-${precision}`);
3
+ return this > (other - delta) && this < (other + delta);
4
+ //return this.toFixed(precision) === other.toFixed(precision);
5
+ };
6
+ Number.prototype.roundoff = function (precision = 14) {
7
+ return parseFloat(this.toFixed(precision));
8
+ };
9
+ Number.prototype.clamp = function (min, max) {
10
+ let value = this;
11
+ if (min != null && !isNaN(min)) {
12
+ value = Math.max(value, min);
13
+ }
14
+ if (max != null && !isNaN(max)) {
15
+ value = Math.min(value, max);
16
+ }
17
+ return value;
18
+ };
19
+ Number.prototype.toBase62 = function () {
20
+ var alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
21
+ var temp = this;
22
+ var sb = '';
23
+ while (temp != 0) {
24
+ var mod = temp % 62;
25
+ sb = alphabet.charAt(mod) + sb;
26
+ temp = Math.floor(temp / 62);
27
+ }
28
+ return sb;
29
+ };
30
+ Number.prototype.toBase36 = function () {
31
+ return this.toString(36);
32
+ };
33
+ export {};
@@ -0,0 +1,18 @@
1
+ // namespace Pacem {
2
+ export class Numbers {
3
+ static round(x, step = 1.0) {
4
+ return Math.round(x / step) * step;
5
+ }
6
+ static log(x, base) {
7
+ return Math.log10(x) / Math.log10(base);
8
+ }
9
+ static rebase(v, from, to) {
10
+ const vStr = v.toString();
11
+ const firstPass = parseInt(vStr, from);
12
+ if (!vStr.endsWith(firstPass.toString(from))) {
13
+ return Number.NaN.toString();
14
+ }
15
+ return parseInt(v.toString(), from).toString(to);
16
+ }
17
+ }
18
+ //}
@@ -0,0 +1,128 @@
1
+ import { Matrix2D } from './matrix2d';
2
+ import { parseAsNumericalArray, Point, Size } from "./geom";
3
+ export class Rect {
4
+ static parse(rect) {
5
+ let arr = parseAsNumericalArray(rect);
6
+ if (arr && arr.length === 4) {
7
+ return { x: arr[0], y: arr[1], width: arr[2], height: arr[3] };
8
+ }
9
+ throw new Error(`Cannot parse "${rect}" as a valid Rect.`);
10
+ }
11
+ static isRect(obj) {
12
+ return Point.isPoint(obj) && Size.isSize(obj);
13
+ }
14
+ static _stretch(from, to) {
15
+ return { a: to.width / from.width, b: 0, c: 0, d: to.height / from.height, e: to.x - from.x, f: to.y - from.y };
16
+ }
17
+ static _contain(from, to, align, valign) {
18
+ let scale = 1;
19
+ const fromRatio = from.width / from.height, toRatio = to.width / to.height;
20
+ if (fromRatio >= toRatio) {
21
+ // constraining width
22
+ scale = to.width / from.width;
23
+ }
24
+ else {
25
+ // constraining height
26
+ scale = to.height / from.height;
27
+ }
28
+ const w = from.width * scale, h = from.height * scale;
29
+ const translation = this._align({ x: from.x, y: from.y, width: w, height: h }, to, align, valign);
30
+ return Matrix2D.translate({ a: scale, b: 0, c: 0, d: scale, e: 0, f: 0 }, translation);
31
+ }
32
+ static _cover(from, to, align, valign) {
33
+ let scale = 1;
34
+ const fromRatio = from.width / from.height, toRatio = to.width / to.height;
35
+ if (fromRatio < toRatio) {
36
+ // constraining width
37
+ scale = to.width / from.width;
38
+ }
39
+ else {
40
+ // constraining height
41
+ scale = to.height / from.height;
42
+ }
43
+ const w = from.width * scale, h = from.height * scale;
44
+ const translation = this._align({ x: from.x, y: from.y, width: w, height: h }, to, align, valign);
45
+ return Matrix2D.translate({ a: scale, b: 0, c: 0, d: scale, e: 0, f: 0 }, translation);
46
+ }
47
+ static _align(from, to, align, valign) {
48
+ let x = 0, y = 0;
49
+ switch (align) {
50
+ case 'left':
51
+ x = to.x - from.x;
52
+ break;
53
+ case 'right':
54
+ x = (to.x + to.width - from.width) - from.x;
55
+ break;
56
+ default:
57
+ x = (to.x + to.width * .5 - from.width * .5) - from.x;
58
+ break;
59
+ }
60
+ switch (valign) {
61
+ case 'top':
62
+ y = to.y - from.y;
63
+ break;
64
+ case 'bottom':
65
+ y = (to.y + to.height - from.height) - from.y;
66
+ break;
67
+ default:
68
+ y = (to.y + to.height * .5 - from.height * .5) - from.y;
69
+ break;
70
+ }
71
+ return { x, y };
72
+ }
73
+ /**
74
+ * Computes the 2D matrix that transforms one rect into another.
75
+ * @param a Rect from
76
+ * @param b Rect to
77
+ * @param mode Transformation mode
78
+ * @param align Horizontal alignment
79
+ * @param valign Vertical alignment
80
+ */
81
+ static findTransform(a, b, mode = 'stretch', align = 'center', valign = 'middle') {
82
+ switch (mode) {
83
+ case 'contain':
84
+ return this._contain(a, b, align, valign);
85
+ case 'cover':
86
+ return this._cover(a, b, align, valign);
87
+ default:
88
+ return this._stretch(a, b);
89
+ }
90
+ }
91
+ /**
92
+ * Computes the intersection rect among a set of provided rects.
93
+ * @param rects Rects to intersect
94
+ */
95
+ static intersect(...rects) {
96
+ const empty = { x: 0, y: 0, height: 0, width: 0 }, arr = Array.from(rects || []);
97
+ if (arr.length <= 0) {
98
+ return empty;
99
+ }
100
+ var rect = arr[0];
101
+ for (var i = 1; i < arr.length; i++) {
102
+ const r = arr[i];
103
+ const x = Math.max(r.x, rect.x), y = Math.max(r.y, rect.y), xw = Math.min(r.x + r.width, rect.x + rect.width), xh = Math.min(r.y + r.height, rect.y + rect.height), w = xw - x, h = xh - y;
104
+ if (w < 0 || h <= 0) {
105
+ // no intersection
106
+ return empty;
107
+ }
108
+ rect = { x: x, y: y, width: w, height: h };
109
+ }
110
+ return rect;
111
+ }
112
+ static expand(rect, ...args) {
113
+ const arr = Array.from(args || []);
114
+ let x0 = rect.x, y0 = rect.y, x1 = x0, y1 = y0;
115
+ if (Rect.isRect(rect)) {
116
+ x1 += rect.width;
117
+ y1 += rect.height;
118
+ }
119
+ for (let p of arr) {
120
+ x0 = Math.min(p.x, x0);
121
+ y0 = Math.min(p.y, y0);
122
+ x1 = Math.max(p.x, x1);
123
+ y1 = Math.max(p.y, y1);
124
+ }
125
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
126
+ }
127
+ }
128
+ //}
@@ -0,0 +1,184 @@
1
+ // namespace Pacem {
2
+ import { NullChecker } from "./null-checker";
3
+ /**
4
+ * Computes the Longest Common Subsequence (LCS) table for two arrays of strings.
5
+ * @param aTokens Array of strings from the first input.
6
+ * @param bTokens Array of strings from the second input.
7
+ * @returns The DP table for LCS lengths.
8
+ */
9
+ function lcs(aTokens, bTokens) {
10
+ const m = aTokens.length;
11
+ const n = bTokens.length;
12
+ // Initialize DP table with zeros
13
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
14
+ // Fill DP table
15
+ for (let i = 1; i <= m; i++) {
16
+ for (let j = 1; j <= n; j++) {
17
+ if (aTokens[i - 1] === bTokens[j - 1]) {
18
+ // Tokens match, increment LCS length
19
+ dp[i][j] = dp[i - 1][j - 1] + 1;
20
+ }
21
+ else {
22
+ // Tokens don't match, take max length from previous states
23
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
24
+ }
25
+ }
26
+ }
27
+ return dp;
28
+ }
29
+ /**
30
+ * Internal diff implementation using backtracking on the LCS DP table.
31
+ * Works on token arrays (characters, words, lines).
32
+ * @param aTokens Array of strings from the first input.
33
+ * @param bTokens Array of strings from the second input.
34
+ * @param dp The precomputed LCS DP table.
35
+ * @param joiner The string to use when joining tokens of the same type (e.g., "" for chars, "\n" for lines).
36
+ * For word diffing, joiner is effectively "" because whitespace is part of the tokens.
37
+ * @returns An array of DiffTokens representing the differences.
38
+ */
39
+ function diffInternal(aTokens, bTokens, dp, joiner = '') {
40
+ const m = aTokens.length;
41
+ const n = bTokens.length;
42
+ const result = [];
43
+ let i = m, j = n;
44
+ let currentToken = null;
45
+ const finalizeToken = () => {
46
+ if (currentToken) {
47
+ result.push(currentToken);
48
+ currentToken = null;
49
+ }
50
+ };
51
+ // Backtrack through the DP table
52
+ while (i > 0 || j > 0) { // Continue until both sequences are fully processed
53
+ var currentType = null;
54
+ let currentValue = null;
55
+ if (i > 0 && j > 0 && aTokens[i - 1] === bTokens[j - 1]) {
56
+ // Common token
57
+ currentType = '=';
58
+ currentValue = aTokens[i - 1];
59
+ i--;
60
+ j--;
61
+ }
62
+ else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
63
+ // Token added in b
64
+ currentType = '+';
65
+ currentValue = bTokens[j - 1];
66
+ j--;
67
+ }
68
+ else if (i > 0 && (j === 0 || dp[i - 1][j] > dp[i][j - 1])) {
69
+ // Token removed from a
70
+ currentType = '-';
71
+ currentValue = aTokens[i - 1];
72
+ i--;
73
+ }
74
+ if (currentType && currentValue !== null) {
75
+ if (currentToken && currentToken.type === currentType) {
76
+ // Prepend the value to the existing token
77
+ currentToken.value = currentValue + (currentToken.value ? joiner : '') + currentToken.value;
78
+ }
79
+ else {
80
+ // Finalize the previous token (if different type) and start a new one
81
+ finalizeToken();
82
+ currentToken = { type: currentType, value: currentValue };
83
+ }
84
+ }
85
+ else {
86
+ // Should not happen in standard LCS backtracking, but finalize just in case
87
+ finalizeToken();
88
+ }
89
+ }
90
+ finalizeToken(); // Add the last accumulated token
91
+ return result.reverse(); // Reverse to get the correct order
92
+ }
93
+ /**
94
+ * Performs character-level diffing.
95
+ * @param a The first string.
96
+ * @param b The second string.
97
+ * @returns An array of DiffTokens.
98
+ */
99
+ function diffChars(a, b) {
100
+ const aTokens = a.split(''); // Split into characters
101
+ const bTokens = b.split(''); // Split into characters
102
+ const dp = lcs(aTokens, bTokens);
103
+ // No joiner needed as characters are concatenated directly
104
+ return diffInternal(aTokens, bTokens, dp, '');
105
+ }
106
+ /**
107
+ * Performs word-level diffing, preserving whitespace and line breaks.
108
+ * It tokenizes based on sequences of non-whitespace (\S+) or whitespace (\s+).
109
+ * @param a The first string.
110
+ * @param b The second string.
111
+ * @returns An array of DiffTokens.
112
+ */
113
+ function diffWords(a, b) {
114
+ // Tokenize into words (\S+) and whitespace/newlines (\s+)
115
+ // This preserves the original spacing and line breaks.
116
+ const wordRegex = /(\S+|\s+)/g;
117
+ const aTokens = a.match(wordRegex) || [];
118
+ const bTokens = b.match(wordRegex) || [];
119
+ const dp = lcs(aTokens, bTokens);
120
+ // No joiner needed; whitespace tokens handle spacing.
121
+ return diffInternal(aTokens, bTokens, dp, '');
122
+ }
123
+ /**
124
+ * Performs line-level diffing.
125
+ * @param a The first string.
126
+ * @param b The second string.
127
+ * @returns An array of DiffTokens where each token's value can contain multiple lines.
128
+ */
129
+ function diffLines(a, b) {
130
+ // Split by newline characters, keeping the EOL character concept separate
131
+ // The result of split does not contain the separators themselves.
132
+ const lineRegex = /\r?\n/;
133
+ const aTokens = a.split(lineRegex);
134
+ const bTokens = b.split(lineRegex);
135
+ // Handle potential empty string added by split if text ends with a newline
136
+ // (This behavior is often desired, representing an empty line after the last newline)
137
+ // Example: "a\nb\n".split(/\n/) -> ["a", "b", ""]
138
+ const dp = lcs(aTokens, bTokens);
139
+ // Join consecutive lines of the same type with newline characters
140
+ return diffInternal(aTokens, bTokens, dp, '\n');
141
+ }
142
+ //#endregion
143
+ export class Strings {
144
+ /** @deprecated Use String.prototype.padStart() instead. */
145
+ static leftPad(v, targetLength, padChar) {
146
+ let retval = v.toString();
147
+ return retval.padStart(targetLength, padChar);
148
+ //while (retval.length < targetLength)
149
+ // retval = padChar + retval;
150
+ //return retval;
151
+ }
152
+ static format(template, ...args) {
153
+ const array = Array.from(args);
154
+ let retval = template;
155
+ for (let j = 0; j < array.length; j++) {
156
+ const regex = new RegExp('(^|[^\\{])\\{' + j + '\\}($|[^\\}])', 'g');
157
+ retval = retval.replaceAll(regex, (match, ...args0) => {
158
+ return match.replace('{' + j + '}', array[j]);
159
+ });
160
+ }
161
+ const check = /(^|[^\{}])\{(\d+)\}($|[^\}])/.exec(retval);
162
+ if (!NullChecker.isNullOrEmpty(check)) {
163
+ throw new Error(`Missing argument at index ${check[2]}.`);
164
+ }
165
+ return retval;
166
+ }
167
+ static diff(a, b, mode = 'char') {
168
+ switch (mode) {
169
+ case 'line':
170
+ return diffLines(a, b);
171
+ case 'word':
172
+ return diffWords(a, b);
173
+ default:
174
+ return diffChars(a, b);
175
+ }
176
+ }
177
+ static async hash(input, algo = 'SHA-256') {
178
+ const hashBuffer = await crypto.subtle.digest(algo, new TextEncoder().encode(input));
179
+ return Array.from(new Uint8Array(hashBuffer))
180
+ .map(byte => byte.toString(16).padStart(2, '0'))
181
+ .join('');
182
+ }
183
+ }
184
+ // }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@pacem/pacem-foundation",
3
+ "files": [
4
+ "dist/browser/*.*",
5
+ "dist/bundle/*.*",
6
+ "dist/esm/*.*",
7
+ "typings/*.*",
8
+ "README.md",
9
+ "LICENSE",
10
+ "NOTICE",
11
+ "package.json"
12
+ ],
13
+ "type": "module",
14
+ "main": "./dist/esm/index.js",
15
+ "module": "./dist/esm/index.js",
16
+ "typings": "./typings/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/esm/index.js",
20
+ "types": "./typings/index.d.ts"
21
+ },
22
+ "./bundle": {
23
+ "import": "./dist/bundle/pacem-foundation.mjs",
24
+ "types": "./typings/index.d.ts"
25
+ }
26
+ },
27
+ "version": "1.0.0-abel",
28
+ "author": {
29
+ "name": "Pacem",
30
+ "url": "https://pacem.it"
31
+ },
32
+ "license": "Apache-2.0",
33
+ "homepage": "https://js.pacem.it",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/pacem-it/pacem.git"
37
+ }
38
+ }