@byline/db-mysql 4.8.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.
Files changed (75) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +283 -0
  3. package/dist/database/schema/auth.d.ts +951 -0
  4. package/dist/database/schema/auth.js +226 -0
  5. package/dist/database/schema/common.d.ts +168 -0
  6. package/dist/database/schema/common.js +130 -0
  7. package/dist/database/schema/index.d.ts +3419 -0
  8. package/dist/database/schema/index.js +895 -0
  9. package/dist/database/schema/schema-pins.test.node.d.ts +52 -0
  10. package/dist/database/schema/schema-pins.test.node.js +398 -0
  11. package/dist/index.d.ts +79 -0
  12. package/dist/index.js +151 -0
  13. package/dist/lib/boot-check.d.ts +22 -0
  14. package/dist/lib/boot-check.js +42 -0
  15. package/dist/lib/boot-check.test.node.d.ts +8 -0
  16. package/dist/lib/boot-check.test.node.js +46 -0
  17. package/dist/lib/db-manager.d.ts +54 -0
  18. package/dist/lib/db-manager.js +49 -0
  19. package/dist/lib/test-db.d.ts +37 -0
  20. package/dist/lib/test-db.js +111 -0
  21. package/dist/lib/test-helper.d.ts +33 -0
  22. package/dist/lib/test-helper.js +68 -0
  23. package/dist/modules/admin/admin-permissions-repository.d.ts +35 -0
  24. package/dist/modules/admin/admin-permissions-repository.js +101 -0
  25. package/dist/modules/admin/admin-preferences-repository.d.ts +62 -0
  26. package/dist/modules/admin/admin-preferences-repository.js +156 -0
  27. package/dist/modules/admin/admin-roles-repository.d.ts +11 -0
  28. package/dist/modules/admin/admin-roles-repository.js +209 -0
  29. package/dist/modules/admin/admin-store.d.ts +20 -0
  30. package/dist/modules/admin/admin-store.js +30 -0
  31. package/dist/modules/admin/admin-users-repository.d.ts +11 -0
  32. package/dist/modules/admin/admin-users-repository.js +303 -0
  33. package/dist/modules/admin/index.d.ts +27 -0
  34. package/dist/modules/admin/index.js +27 -0
  35. package/dist/modules/admin/refresh-tokens-repository.d.ts +34 -0
  36. package/dist/modules/admin/refresh-tokens-repository.js +160 -0
  37. package/dist/modules/audit/audit-commands.d.ts +29 -0
  38. package/dist/modules/audit/audit-commands.js +40 -0
  39. package/dist/modules/audit/audit-queries.d.ts +41 -0
  40. package/dist/modules/audit/audit-queries.js +169 -0
  41. package/dist/modules/counters/counters-commands.d.ts +54 -0
  42. package/dist/modules/counters/counters-commands.js +121 -0
  43. package/dist/modules/counters/tests/counters-concurrency.test.d.ts +8 -0
  44. package/dist/modules/counters/tests/counters-concurrency.test.js +111 -0
  45. package/dist/modules/storage/classify-error.d.ts +34 -0
  46. package/dist/modules/storage/classify-error.js +50 -0
  47. package/dist/modules/storage/classify-error.test.node.d.ts +8 -0
  48. package/dist/modules/storage/classify-error.test.node.js +89 -0
  49. package/dist/modules/storage/normalize-row.d.ts +55 -0
  50. package/dist/modules/storage/normalize-row.js +135 -0
  51. package/dist/modules/storage/normalize-row.test.node.d.ts +8 -0
  52. package/dist/modules/storage/normalize-row.test.node.js +89 -0
  53. package/dist/modules/storage/storage-commands.d.ts +443 -0
  54. package/dist/modules/storage/storage-commands.js +1263 -0
  55. package/dist/modules/storage/storage-insert.d.ts +21 -0
  56. package/dist/modules/storage/storage-insert.js +152 -0
  57. package/dist/modules/storage/storage-queries.d.ts +805 -0
  58. package/dist/modules/storage/storage-queries.js +1815 -0
  59. package/dist/modules/storage/storage-store-manifest.d.ts +77 -0
  60. package/dist/modules/storage/storage-store-manifest.js +168 -0
  61. package/dist/modules/storage/storage-utils.d.ts +49 -0
  62. package/dist/modules/storage/storage-utils.js +76 -0
  63. package/dist/modules/storage/tests/dialect-pins.integration.test.d.ts +8 -0
  64. package/dist/modules/storage/tests/dialect-pins.integration.test.js +266 -0
  65. package/dist/modules/storage/tests/storage-commands.test.d.ts +8 -0
  66. package/dist/modules/storage/tests/storage-commands.test.js +324 -0
  67. package/dist/modules/storage/tests/storage-document-paths.test.d.ts +8 -0
  68. package/dist/modules/storage/tests/storage-document-paths.test.js +214 -0
  69. package/dist/modules/storage/tests/storage-document-tree.test.d.ts +8 -0
  70. package/dist/modules/storage/tests/storage-document-tree.test.js +361 -0
  71. package/dist/modules/storage/tests/storage-queries.test.d.ts +8 -0
  72. package/dist/modules/storage/tests/storage-queries.test.js +685 -0
  73. package/dist/modules/storage/tests/storage-status-and-lifecycle.test.d.ts +8 -0
  74. package/dist/modules/storage/tests/storage-status-and-lifecycle.test.js +268 -0
  75. package/package.json +91 -0
@@ -0,0 +1,361 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /**
9
+ * Live-database verification of the Task 9B document-tree command surface —
10
+ * `DocumentCommands.placeTreeNode`, `removeFromTree`, and
11
+ * `promoteChildrenAndRemoveFromTree`. Mirrors the write-half of
12
+ * `@byline/db-conformance`'s `document-tree` suite
13
+ * (`packages/db-conformance/src/suites/document-tree.ts`), but that suite
14
+ * also exercises `getTreeChildren` / `getTreeAncestors` / `getTreeSubtree`
15
+ * (Task 10 read surface, not implemented yet) — so every assertion here reads
16
+ * `byline_document_relationships` directly via raw SQL instead, and only
17
+ * behaviours observable from the write commands alone are covered. Once
18
+ * Task 10 lands, `document-tree` becomes runnable end to end and is the
19
+ * authoritative gate; this file is the interim live-database regression
20
+ * guard for the command half.
21
+ */
22
+ import { ErrorCodes, TREE_PLACEMENT_STALE_MARKER } from '@byline/core';
23
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
24
+ import { setupTestDB, teardownTestDB } from '../../../lib/test-helper.js';
25
+ const timestamp = Date.now();
26
+ function first(rows) {
27
+ const row = rows[0];
28
+ if (row == null)
29
+ throw new Error('expected at least one row, got none');
30
+ return row;
31
+ }
32
+ async function queryRows(pool, sql, params) {
33
+ const [rows] = await pool.query(sql, params);
34
+ return rows;
35
+ }
36
+ /** Read one ordered sibling group directly from `byline_document_relationships`. */
37
+ async function siblingIds(pool, parentDocumentId) {
38
+ // MySQL's null-safe equality (`<=>`) so `parentDocumentId: null` matches
39
+ // NULL rows (root siblings) the same way `parent_document_id IS NULL` does.
40
+ const rows = await queryRows(pool, 'SELECT child_document_id FROM byline_document_relationships WHERE parent_document_id <=> ? ORDER BY order_key', [parentDocumentId]);
41
+ return rows.map((r) => r.child_document_id);
42
+ }
43
+ async function edgeRow(pool, documentId) {
44
+ const rows = await queryRows(pool, 'SELECT child_document_id, parent_document_id, order_key FROM byline_document_relationships WHERE child_document_id = ?', [documentId]);
45
+ return rows[0];
46
+ }
47
+ const TreeCollectionConfig = {
48
+ path: `tree-test-${timestamp}`,
49
+ labels: { singular: 'TreeTest', plural: 'TreeTests' },
50
+ fields: [{ name: 'title', type: 'text' }],
51
+ };
52
+ const OtherCollectionConfig = {
53
+ path: `tree-other-test-${timestamp}`,
54
+ labels: { singular: 'TreeOther', plural: 'TreeOthers' },
55
+ fields: [{ name: 'title', type: 'text' }],
56
+ };
57
+ describe('DocumentCommands tree mutations (mysql, live database)', () => {
58
+ let testDb;
59
+ let rawPool;
60
+ let treeCollectionId;
61
+ let otherCollectionId;
62
+ async function createDoc(collectionId, config, title) {
63
+ const created = await testDb.commandBuilders.documents.createDocumentVersion({
64
+ collectionId,
65
+ collectionVersion: 1,
66
+ collectionConfig: config,
67
+ action: 'create',
68
+ documentData: { title },
69
+ locale: 'all',
70
+ status: 'published',
71
+ });
72
+ return created.document.document_id;
73
+ }
74
+ beforeAll(async () => {
75
+ testDb = setupTestDB([TreeCollectionConfig, OtherCollectionConfig]);
76
+ rawPool = testDb.pool;
77
+ treeCollectionId = first(await testDb.commandBuilders.collections.create(TreeCollectionConfig.path, TreeCollectionConfig)).id;
78
+ otherCollectionId = first(await testDb.commandBuilders.collections.create(OtherCollectionConfig.path, OtherCollectionConfig)).id;
79
+ });
80
+ afterAll(async () => {
81
+ await testDb.commandBuilders.collections.delete(treeCollectionId);
82
+ await testDb.commandBuilders.collections.delete(otherCollectionId);
83
+ await teardownTestDB();
84
+ });
85
+ it('places roots and children, ordered per-parent', async () => {
86
+ const a = await createDoc(treeCollectionId, TreeCollectionConfig, 'Root A');
87
+ const b = await createDoc(treeCollectionId, TreeCollectionConfig, 'Root B');
88
+ const c = await createDoc(treeCollectionId, TreeCollectionConfig, 'Child C');
89
+ const d = await createDoc(treeCollectionId, TreeCollectionConfig, 'Child D');
90
+ await testDb.commandBuilders.documents.placeTreeNode({
91
+ collectionId: treeCollectionId,
92
+ documentId: a,
93
+ parentDocumentId: null,
94
+ });
95
+ await testDb.commandBuilders.documents.placeTreeNode({
96
+ collectionId: treeCollectionId,
97
+ documentId: b,
98
+ parentDocumentId: null,
99
+ beforeDocumentId: a,
100
+ });
101
+ expect(await siblingIds(rawPool, null)).toEqual(expect.arrayContaining([a, b]));
102
+ const roots = await siblingIds(rawPool, null);
103
+ expect(roots.indexOf(a)).toBeLessThan(roots.indexOf(b));
104
+ await testDb.commandBuilders.documents.placeTreeNode({
105
+ collectionId: treeCollectionId,
106
+ documentId: c,
107
+ parentDocumentId: a,
108
+ });
109
+ await testDb.commandBuilders.documents.placeTreeNode({
110
+ collectionId: treeCollectionId,
111
+ documentId: d,
112
+ parentDocumentId: a,
113
+ beforeDocumentId: c,
114
+ });
115
+ expect(await siblingIds(rawPool, a)).toEqual([c, d]);
116
+ });
117
+ it('reorders siblings in place — upsert keeps one row per document', async () => {
118
+ const a = await createDoc(treeCollectionId, TreeCollectionConfig, 'P A');
119
+ const x = await createDoc(treeCollectionId, TreeCollectionConfig, 'X');
120
+ const y = await createDoc(treeCollectionId, TreeCollectionConfig, 'Y');
121
+ await testDb.commandBuilders.documents.placeTreeNode({
122
+ collectionId: treeCollectionId,
123
+ documentId: a,
124
+ parentDocumentId: null,
125
+ });
126
+ await testDb.commandBuilders.documents.placeTreeNode({
127
+ collectionId: treeCollectionId,
128
+ documentId: x,
129
+ parentDocumentId: a,
130
+ });
131
+ await testDb.commandBuilders.documents.placeTreeNode({
132
+ collectionId: treeCollectionId,
133
+ documentId: y,
134
+ parentDocumentId: a,
135
+ beforeDocumentId: x,
136
+ });
137
+ expect(await siblingIds(rawPool, a)).toEqual([x, y]);
138
+ await testDb.commandBuilders.documents.placeTreeNode({
139
+ collectionId: treeCollectionId,
140
+ documentId: y,
141
+ parentDocumentId: a,
142
+ afterDocumentId: x,
143
+ });
144
+ const kids = await siblingIds(rawPool, a);
145
+ expect(kids).toEqual([y, x]);
146
+ expect(kids.length).toBe(2); // no duplicate row from the upsert
147
+ });
148
+ it('rejects a stale target neighbour group as a conflict', async () => {
149
+ const parentA = await createDoc(treeCollectionId, TreeCollectionConfig, 'Conflict Parent A');
150
+ const parentB = await createDoc(treeCollectionId, TreeCollectionConfig, 'Conflict Parent B');
151
+ const neighbour = await createDoc(treeCollectionId, TreeCollectionConfig, 'Conflict Neighbour');
152
+ const node = await createDoc(treeCollectionId, TreeCollectionConfig, 'Conflict Node');
153
+ await testDb.commandBuilders.documents.placeTreeNode({
154
+ collectionId: treeCollectionId,
155
+ documentId: neighbour,
156
+ parentDocumentId: parentA,
157
+ });
158
+ await testDb.commandBuilders.documents.placeTreeNode({
159
+ collectionId: treeCollectionId,
160
+ documentId: neighbour,
161
+ parentDocumentId: parentB,
162
+ });
163
+ await expect(testDb.commandBuilders.documents.placeTreeNode({
164
+ collectionId: treeCollectionId,
165
+ documentId: node,
166
+ parentDocumentId: parentA,
167
+ beforeDocumentId: neighbour,
168
+ })).rejects.toMatchObject({
169
+ code: ErrorCodes.CONFLICT,
170
+ message: expect.stringContaining(TREE_PLACEMENT_STALE_MARKER),
171
+ });
172
+ });
173
+ it('allows only one concurrent placement into the same asserted sibling gap', async () => {
174
+ const parent = await createDoc(treeCollectionId, TreeCollectionConfig, 'Gap Parent');
175
+ const left = await createDoc(treeCollectionId, TreeCollectionConfig, 'Gap Left');
176
+ const right = await createDoc(treeCollectionId, TreeCollectionConfig, 'Gap Right');
177
+ const nodeFirst = await createDoc(treeCollectionId, TreeCollectionConfig, 'Gap First');
178
+ const nodeSecond = await createDoc(treeCollectionId, TreeCollectionConfig, 'Gap Second');
179
+ await testDb.commandBuilders.documents.placeTreeNode({
180
+ collectionId: treeCollectionId,
181
+ documentId: left,
182
+ parentDocumentId: parent,
183
+ });
184
+ await testDb.commandBuilders.documents.placeTreeNode({
185
+ collectionId: treeCollectionId,
186
+ documentId: right,
187
+ parentDocumentId: parent,
188
+ beforeDocumentId: left,
189
+ });
190
+ const placeInSameGap = (documentId) => testDb.commandBuilders.documents.placeTreeNode({
191
+ collectionId: treeCollectionId,
192
+ documentId,
193
+ parentDocumentId: parent,
194
+ beforeDocumentId: left,
195
+ afterDocumentId: right,
196
+ });
197
+ const results = await Promise.allSettled([
198
+ placeInSameGap(nodeFirst),
199
+ placeInSameGap(nodeSecond),
200
+ ]);
201
+ const fulfilled = results.filter((r) => r.status === 'fulfilled');
202
+ const rejected = results.filter((r) => r.status === 'rejected');
203
+ expect(fulfilled).toHaveLength(1);
204
+ expect(rejected).toHaveLength(1);
205
+ expect(rejected[0]).toMatchObject({ reason: { code: ErrorCodes.CONFLICT } });
206
+ const winner = results[0]?.status === 'fulfilled' ? nodeFirst : nodeSecond;
207
+ const loser = winner === nodeFirst ? nodeSecond : nodeFirst;
208
+ expect(await siblingIds(rawPool, parent)).toEqual([left, winner, right]);
209
+ expect(await edgeRow(rawPool, loser)).toBeUndefined(); // never placed
210
+ });
211
+ it('rejects placement when the moving node was deleted after the placement snapshot', async () => {
212
+ const node = await createDoc(treeCollectionId, TreeCollectionConfig, 'Deleted Moving Node');
213
+ await testDb.commandBuilders.documents.placeTreeNode({
214
+ collectionId: treeCollectionId,
215
+ documentId: node,
216
+ parentDocumentId: null,
217
+ });
218
+ await testDb.commandBuilders.documents.softDeleteDocument({ document_id: node });
219
+ await expect(testDb.commandBuilders.documents.placeTreeNode({
220
+ collectionId: treeCollectionId,
221
+ documentId: node,
222
+ parentDocumentId: null,
223
+ })).rejects.toMatchObject({ code: ErrorCodes.CONFLICT });
224
+ });
225
+ it('rejects a cross-collection neighbour as structural validation', async () => {
226
+ const parent = await createDoc(treeCollectionId, TreeCollectionConfig, 'Foreign Neighbour Parent');
227
+ const node = await createDoc(treeCollectionId, TreeCollectionConfig, 'Foreign Neighbour Node');
228
+ const foreign = await createDoc(otherCollectionId, OtherCollectionConfig, 'Foreign Neighbour');
229
+ await expect(testDb.commandBuilders.documents.placeTreeNode({
230
+ collectionId: treeCollectionId,
231
+ documentId: node,
232
+ parentDocumentId: parent,
233
+ afterDocumentId: foreign,
234
+ })).rejects.toMatchObject({ code: ErrorCodes.VALIDATION });
235
+ });
236
+ it('rejects a self-parent and a cycle', async () => {
237
+ const a = await createDoc(treeCollectionId, TreeCollectionConfig, 'CA');
238
+ const c = await createDoc(treeCollectionId, TreeCollectionConfig, 'CC');
239
+ await testDb.commandBuilders.documents.placeTreeNode({
240
+ collectionId: treeCollectionId,
241
+ documentId: a,
242
+ parentDocumentId: null,
243
+ });
244
+ await testDb.commandBuilders.documents.placeTreeNode({
245
+ collectionId: treeCollectionId,
246
+ documentId: c,
247
+ parentDocumentId: a,
248
+ });
249
+ await expect(testDb.commandBuilders.documents.placeTreeNode({
250
+ collectionId: treeCollectionId,
251
+ documentId: a,
252
+ parentDocumentId: a,
253
+ })).rejects.toMatchObject({ code: ErrorCodes.VALIDATION });
254
+ // A is C's ancestor, so making A a child of C is a cycle — exercises the
255
+ // `WITH RECURSIVE chain AS (...)` cycle guard.
256
+ await expect(testDb.commandBuilders.documents.placeTreeNode({
257
+ collectionId: treeCollectionId,
258
+ documentId: a,
259
+ parentDocumentId: c,
260
+ })).rejects.toMatchObject({ code: ErrorCodes.VALIDATION });
261
+ });
262
+ it('re-parents atomically — one edge row, updated in place', async () => {
263
+ const a = await createDoc(treeCollectionId, TreeCollectionConfig, 'RA');
264
+ const b = await createDoc(treeCollectionId, TreeCollectionConfig, 'RB');
265
+ const c = await createDoc(treeCollectionId, TreeCollectionConfig, 'RC');
266
+ await testDb.commandBuilders.documents.placeTreeNode({
267
+ collectionId: treeCollectionId,
268
+ documentId: a,
269
+ parentDocumentId: null,
270
+ });
271
+ await testDb.commandBuilders.documents.placeTreeNode({
272
+ collectionId: treeCollectionId,
273
+ documentId: b,
274
+ parentDocumentId: null,
275
+ });
276
+ await testDb.commandBuilders.documents.placeTreeNode({
277
+ collectionId: treeCollectionId,
278
+ documentId: c,
279
+ parentDocumentId: a,
280
+ });
281
+ expect(await siblingIds(rawPool, a)).toEqual([c]);
282
+ await testDb.commandBuilders.documents.placeTreeNode({
283
+ collectionId: treeCollectionId,
284
+ documentId: c,
285
+ parentDocumentId: b,
286
+ });
287
+ expect(await siblingIds(rawPool, a)).toEqual([]);
288
+ expect(await siblingIds(rawPool, b)).toEqual([c]);
289
+ const edge = await edgeRow(rawPool, c);
290
+ expect(edge.parent_document_id).toBe(b);
291
+ });
292
+ it('removeFromTree returns a node to the unplaced state and is idempotent', async () => {
293
+ const a = await createDoc(treeCollectionId, TreeCollectionConfig, 'UA');
294
+ const c = await createDoc(treeCollectionId, TreeCollectionConfig, 'UC');
295
+ await testDb.commandBuilders.documents.placeTreeNode({
296
+ collectionId: treeCollectionId,
297
+ documentId: a,
298
+ parentDocumentId: null,
299
+ });
300
+ await testDb.commandBuilders.documents.placeTreeNode({
301
+ collectionId: treeCollectionId,
302
+ documentId: c,
303
+ parentDocumentId: a,
304
+ });
305
+ expect(await siblingIds(rawPool, a)).toEqual([c]);
306
+ const removed = await testDb.commandBuilders.documents.removeFromTree({
307
+ collectionId: treeCollectionId,
308
+ documentId: c,
309
+ });
310
+ expect(removed.changed).toBe(true);
311
+ expect(await siblingIds(rawPool, a)).toEqual([]);
312
+ expect(await edgeRow(rawPool, c)).toBeUndefined();
313
+ // Idempotent — removing an already-unplaced node is a no-op.
314
+ await expect(testDb.commandBuilders.documents.removeFromTree({
315
+ collectionId: treeCollectionId,
316
+ documentId: c,
317
+ })).resolves.toMatchObject({ changed: false });
318
+ });
319
+ it('promoteChildrenAndRemoveFromTree promotes children to root and removes the parent edge', async () => {
320
+ const parent = await createDoc(treeCollectionId, TreeCollectionConfig, 'Promote Parent');
321
+ const grandparent = await createDoc(treeCollectionId, TreeCollectionConfig, 'Promote Grandparent');
322
+ const childOne = await createDoc(treeCollectionId, TreeCollectionConfig, 'Promote Child 1');
323
+ const childTwo = await createDoc(treeCollectionId, TreeCollectionConfig, 'Promote Child 2');
324
+ await testDb.commandBuilders.documents.placeTreeNode({
325
+ collectionId: treeCollectionId,
326
+ documentId: grandparent,
327
+ parentDocumentId: null,
328
+ });
329
+ await testDb.commandBuilders.documents.placeTreeNode({
330
+ collectionId: treeCollectionId,
331
+ documentId: parent,
332
+ parentDocumentId: grandparent,
333
+ });
334
+ await testDb.commandBuilders.documents.placeTreeNode({
335
+ collectionId: treeCollectionId,
336
+ documentId: childOne,
337
+ parentDocumentId: parent,
338
+ });
339
+ await testDb.commandBuilders.documents.placeTreeNode({
340
+ collectionId: treeCollectionId,
341
+ documentId: childTwo,
342
+ parentDocumentId: parent,
343
+ beforeDocumentId: childOne,
344
+ });
345
+ expect(await siblingIds(rawPool, parent)).toEqual([childOne, childTwo]);
346
+ const result = await testDb.commandBuilders.documents.promoteChildrenAndRemoveFromTree({
347
+ collectionId: treeCollectionId,
348
+ documentId: parent,
349
+ });
350
+ expect(result.removed.changed).toBe(true);
351
+ expect(result.promoted.map((p) => p.documentId).sort()).toEqual([childOne, childTwo].sort());
352
+ // Parent edge gone; both children now root-level (parent_document_id NULL).
353
+ expect(await edgeRow(rawPool, parent)).toBeUndefined();
354
+ const oneEdge = await edgeRow(rawPool, childOne);
355
+ const twoEdge = await edgeRow(rawPool, childTwo);
356
+ expect(oneEdge.parent_document_id).toBeNull();
357
+ expect(twoEdge.parent_document_id).toBeNull();
358
+ // No longer children of the (now-removed) parent edge.
359
+ expect(await siblingIds(rawPool, parent)).toEqual([]);
360
+ });
361
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};