@miso.ai/lorem 0.0.1

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/bin/data.js ADDED
@@ -0,0 +1,15 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { dirname, resolve } from 'path';
3
+ import { writeFileSync, readFileSync } from 'fs';
4
+ import yaml from 'js-yaml';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ const WORDS_YAML = resolve(__dirname, '../data/words.yaml');
9
+ const WORDS_JS = resolve(__dirname, '../lib/words.js');
10
+
11
+ const words = yaml.load(readFileSync(WORDS_YAML, 'utf8'));
12
+ writeFileSync(WORDS_JS, `
13
+ // genereated by bin/data.js
14
+ export default ${JSON.stringify(words, null, 2)};
15
+ `);
@@ -0,0 +1,178 @@
1
+ - lorem
2
+ - ipsum
3
+ - dolor
4
+ - sit
5
+ - amet
6
+ - consectetur
7
+ - adipiscing
8
+ - elit
9
+ - curabitur
10
+ - vel
11
+ - hendrerit
12
+ - libero
13
+ - eleifend
14
+ - blandit
15
+ - nunc
16
+ - ornare
17
+ - odio
18
+ - ut
19
+ - orci
20
+ - gravida
21
+ - imperdiet
22
+ - nullam
23
+ - purus
24
+ - lacinia
25
+ - a
26
+ - pretium
27
+ - quis
28
+ - congue
29
+ - praesent
30
+ - sagittis
31
+ - laoreet
32
+ - auctor
33
+ - mauris
34
+ - non
35
+ - velit
36
+ - eros
37
+ - dictum
38
+ - proin
39
+ - accumsan
40
+ - sapien
41
+ - nec
42
+ - massa
43
+ - volutpat
44
+ - venenatis
45
+ - sed
46
+ - eu
47
+ - molestie
48
+ - lacus
49
+ - quisque
50
+ - porttitor
51
+ - ligula
52
+ - dui
53
+ - mollis
54
+ - tempus
55
+ - at
56
+ - magna
57
+ - vestibulum
58
+ - turpis
59
+ - ac
60
+ - diam
61
+ - tincidunt
62
+ - id
63
+ - condimentum
64
+ - enim
65
+ - sodales
66
+ - in
67
+ - hac
68
+ - habitasse
69
+ - platea
70
+ - dictumst
71
+ - aenean
72
+ - neque
73
+ - fusce
74
+ - augue
75
+ - leo
76
+ - eget
77
+ - semper
78
+ - mattis
79
+ - tortor
80
+ - scelerisque
81
+ - nulla
82
+ - interdum
83
+ - tellus
84
+ - malesuada
85
+ - rhoncus
86
+ - porta
87
+ - sem
88
+ - aliquet
89
+ - et
90
+ - nam
91
+ - suspendisse
92
+ - potenti
93
+ - vivamus
94
+ - luctus
95
+ - fringilla
96
+ - erat
97
+ - donec
98
+ - justo
99
+ - vehicula
100
+ - ultricies
101
+ - varius
102
+ - ante
103
+ - primis
104
+ - faucibus
105
+ - ultrices
106
+ - posuere
107
+ - cubilia
108
+ - curae
109
+ - etiam
110
+ - cursus
111
+ - aliquam
112
+ - quam
113
+ - dapibus
114
+ - nisl
115
+ - feugiat
116
+ - egestas
117
+ - class
118
+ - aptent
119
+ - taciti
120
+ - sociosqu
121
+ - ad
122
+ - litora
123
+ - torquent
124
+ - per
125
+ - conubia
126
+ - nostra
127
+ - inceptos
128
+ - himenaeos
129
+ - phasellus
130
+ - nibh
131
+ - pulvinar
132
+ - vitae
133
+ - urna
134
+ - iaculis
135
+ - lobortis
136
+ - nisi
137
+ - viverra
138
+ - arcu
139
+ - morbi
140
+ - pellentesque
141
+ - metus
142
+ - commodo
143
+ - ut
144
+ - facilisis
145
+ - felis
146
+ - tristique
147
+ - ullamcorper
148
+ - placerat
149
+ - aenean
150
+ - convallis
151
+ - sollicitudin
152
+ - integer
153
+ - rutrum
154
+ - duis
155
+ - est
156
+ - etiam
157
+ - bibendum
158
+ - donec
159
+ - pharetra
160
+ - vulputate
161
+ - maecenas
162
+ - mi
163
+ - fermentum
164
+ - consequat
165
+ - suscipit
166
+ - aliquam
167
+ - habitant
168
+ - senectus
169
+ - netus
170
+ - fames
171
+ - quisque
172
+ - euismod
173
+ - curabitur
174
+ - lectus
175
+ - elementum
176
+ - tempor
177
+ - risus
178
+ - cras
package/lib/fields.js ADDED
@@ -0,0 +1,114 @@
1
+ import * as lorem from './lorem.js';
2
+ import { imageUrl, sample } from './utils.js';
3
+ import * as md from './markdown.js';
4
+
5
+ const DAY = 1000 * 60 * 60 * 24;
6
+ const WEEK = DAY * 7;
7
+
8
+ export function date({} = {}) {
9
+ const now = Date.now();
10
+ const max = now - DAY;
11
+ const min = max - WEEK * 8;
12
+ return new Date(min + Math.random() * (max - min)).toISOString();
13
+ }
14
+
15
+ export function image({ size = 300 } = {}) {
16
+ return imageUrl(size);
17
+ }
18
+
19
+ export function authors({ size = [1, 3] } = {}) {
20
+ return lorem.lorem({
21
+ size,
22
+ decorates: ['title'],
23
+ output: 'array',
24
+ });
25
+ }
26
+
27
+ export function tags({ size = [1, 4] } = {}) {
28
+ return lorem.lorem({
29
+ size,
30
+ output: 'array',
31
+ });
32
+ }
33
+
34
+ // TODO: categories
35
+
36
+ export function title({ size = [2, 6] } = {}) {
37
+ return lorem.lorem({
38
+ size,
39
+ decorates: ['title'],
40
+ });
41
+ }
42
+
43
+ export function term({ field }) {
44
+ const random = Math.random();
45
+ const size = random < 0.7 ? 1 : random < 0.9 ? 2 : 3;
46
+ return lorem.lorem({
47
+ size,
48
+ decorates: ['title'],
49
+ });
50
+ }
51
+
52
+ export function description({ size = [10, 20], ...options } = {}) {
53
+ const decorator = Object.keys(options).length ? ['description', options] : 'description';
54
+ return lorem.lorem({
55
+ size,
56
+ decorates: [decorator],
57
+ });
58
+ }
59
+
60
+ export function html({ paragraphs = 8, sections, paragraph, image: imageOptions } = {}) {
61
+ paragraph = {
62
+ size: [30, 60],
63
+ ...paragraph,
64
+ };
65
+ imageOptions = {
66
+ size: [480, 270],
67
+ ...imageOptions,
68
+ };
69
+ if (sections === undefined) {
70
+ sections = Math.floor(paragraphs * (1 + Math.random()) / 4);
71
+ }
72
+ sections = Math.max(1, Math.min(sections, paragraphs));
73
+ const content = [];
74
+ const pps = paragraphs / sections;
75
+ let j = 0;
76
+ for (let i = 0; i < sections; i++) {
77
+ if (i > 0) {
78
+ content.push(`<h4>${title()}</h4>`);
79
+ }
80
+ while (j++ < (i + 1) * pps) {
81
+ content.push(`<p>${description(paragraph)}</p>`);
82
+ }
83
+ content.push(`<div class="image-container"><img src="${image(imageOptions)}"></div>`);
84
+ }
85
+ return content.join('');
86
+ }
87
+
88
+ export function availability() {
89
+ return Math.random() > 0.3 ? 'IN_STOCK' : 'OUT_OF_STOCK';
90
+ }
91
+
92
+ export function price() {
93
+ return Math.floor(Math.random() * 10000) / 100;
94
+ }
95
+
96
+ export function rating() {
97
+ return Math.floor(Math.random() * 500) / 100 + 1;
98
+ }
99
+
100
+ export function answer({ sources, format, citation, sampling, features }) {
101
+ switch (format) {
102
+ case 'markdown':
103
+ let index = 1;
104
+ sources = sources.map(({ url }) => ({ url, index: index++ }));
105
+ return md.markdown({ sources, citation, sampling, features });
106
+ case 'plaintext':
107
+ default:
108
+ return lorem.lorem({
109
+ min: sample(50, sampling),
110
+ max: sample(50, sampling),
111
+ decorates: ['description'],
112
+ });
113
+ }
114
+ }
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import * as md from './markdown.js';
2
+ import * as lorem from './lorem.js';
3
+ import * as utils from './utils.js';
4
+ import * as fields from './fields.js';
5
+
6
+ export { md, lorem, utils, fields };
@@ -0,0 +1,162 @@
1
+ export function javascript() {
2
+ return JAVASCRIPT;
3
+ }
4
+
5
+ export function js() {
6
+ return JAVASCRIPT;
7
+ }
8
+
9
+ const JAVASCRIPT = `
10
+ // module
11
+ import { a, b } from './module';
12
+ import * as module from './module';
13
+ import module from './module';
14
+ export default module;
15
+ export const a = 0;
16
+ export * from './module';
17
+ export * as module from './module';
18
+
19
+ // variables
20
+ let a = 10;
21
+ const b = 20;
22
+
23
+ // function declaration
24
+ function sum(x, y) {
25
+ return x + y;
26
+ }
27
+
28
+ // generator function
29
+ function* iterator() {
30
+ yield 0;
31
+ yield 1;
32
+ }
33
+
34
+ // arrow function
35
+ const multiply = (x, y) => x * y;
36
+
37
+ // class
38
+ class Person {
39
+ constructor(name, age) {
40
+ this.name = name;
41
+ this.age = age;
42
+ }
43
+
44
+ greet() {
45
+ console.log('Hello, my name is Miso.');
46
+ }
47
+ }
48
+
49
+ // primitive
50
+ const str = 'Hello, world!';
51
+ const num = 10.99;
52
+
53
+ // object
54
+ const person = new Person('John', 30);
55
+ person.greet();
56
+
57
+ // object literal
58
+ const object = {
59
+ name: 'John',
60
+ [x]: 10,
61
+ ...props,
62
+ };
63
+
64
+ // array literal
65
+ const arr = [1, 2, 3, 4, 5, ...props];
66
+
67
+ // regexp literal
68
+ const regexp = /\\w+/g;
69
+
70
+ // operators
71
+ const sum = a + b;
72
+ const product = a * b;
73
+ const negation = -a;
74
+ const max = a > b ? a : b;
75
+
76
+ // flow control
77
+ let i = 9;
78
+ for (const n of arr) {
79
+ if (n > i) {
80
+ console.log(n);
81
+ }
82
+ i++;
83
+ }
84
+
85
+ // async/await
86
+ (async () => {
87
+ const result = await asyncFunction();
88
+ })();
89
+
90
+ // try/catch
91
+ try {
92
+ } catch (e) {
93
+ }
94
+
95
+ // destructuring
96
+ const { name, age, ...rest } = person;
97
+ const [ x, y, ...rest ] = arr;
98
+
99
+ // template literals
100
+ ${'console.log(`The sum of ${a} and ${b} is ${sum(a, b)}.`);'}
101
+ `.trim();
102
+
103
+ export function html() {
104
+ return HTML;
105
+ }
106
+
107
+ const HTML = `
108
+ <!DOCTYPE html>
109
+ <html lang="en">
110
+ <head>
111
+ <meta charset="UTF-8">
112
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
113
+ <title>Sample Page</title>
114
+ <link rel="stylesheet" href="styles.css">
115
+ </head>
116
+ <body>
117
+ <header>
118
+ <nav>
119
+ <a href="/">Home</a>
120
+ <a href="/about">About</a>
121
+ </nav>
122
+ </header>
123
+
124
+ <main>
125
+ <h1>Welcome</h1>
126
+ <p>This is a <strong>sample</strong> paragraph with <em>emphasis</em>.</p>
127
+
128
+ <ul>
129
+ <li>Item 1</li>
130
+ <li>Item 2</li>
131
+ </ul>
132
+
133
+ <form action="/submit" method="post">
134
+ <label for="name">Name:</label>
135
+ <input type="text" id="name" name="name" required>
136
+ <button type="submit">Submit</button>
137
+ </form>
138
+
139
+ <table>
140
+ <thead>
141
+ <tr>
142
+ <th>Name</th>
143
+ <th>Value</th>
144
+ </tr>
145
+ </thead>
146
+ <tbody>
147
+ <tr>
148
+ <td>Alpha</td>
149
+ <td>100</td>
150
+ </tr>
151
+ </tbody>
152
+ </table>
153
+ </main>
154
+
155
+ <footer>
156
+ <p>&copy; 2024 Example</p>
157
+ </footer>
158
+
159
+ <script src="main.js"></script>
160
+ </body>
161
+ </html>
162
+ `.trim();
package/lib/lorem.js ADDED
@@ -0,0 +1,145 @@
1
+ import { randomInt, gaussRandom, iterateWithLastItemSignal as iterateWithLastItemSignal } from './utils.js';
2
+ import DEFAULT_WORDS from './words.js';
3
+
4
+ export function lorem({ decorates = [], output = 'string', size, min, max, ...options } = {}) {
5
+ let iterator = limit(size || [min, max])(base(options));
6
+ for (const decorate of decorates) {
7
+ iterator = lookup(decorate)(iterator);
8
+ }
9
+ return lookup(output)(iterator);
10
+ }
11
+
12
+ const FNS = {
13
+ string,
14
+ array,
15
+ title,
16
+ description,
17
+ multiline,
18
+ }
19
+
20
+ function lookup(fn) {
21
+ switch (typeof fn) {
22
+ case 'string':
23
+ return FNS[fn]();
24
+ case 'function':
25
+ return fn;
26
+ case 'object':
27
+ if (Array.isArray(fn)) {
28
+ const [name, options = {}] = fn;
29
+ return FNS[name](options);
30
+ }
31
+ }
32
+ throw new Error(`Unrecognized decorator/output form: ${fn}`);
33
+ }
34
+
35
+ // base //
36
+ export function *base({ words = DEFAULT_WORDS, fixedStarts = 0 } = {}) {
37
+ const wordsLength = words.length;
38
+ for (let i = 0; ; i++) {
39
+ yield words[i < fixedStarts ? i : Math.floor(Math.random() * wordsLength)];
40
+ }
41
+ }
42
+
43
+ // output //
44
+ export function string({ separator = ' ' } = {}) {
45
+ return iterator => [...iterator].join(separator);
46
+ }
47
+
48
+ export function array() {
49
+ return iterator => [...iterator];
50
+ }
51
+
52
+ export function multiline({
53
+ wordsPerLine = {
54
+ avg: 10,
55
+ std: 3,
56
+ min: 1,
57
+ },
58
+ } = {}) {
59
+ return iterator => {
60
+ let slen = gaussMS(wordsPerLine);
61
+ let result = '';
62
+ for (let word of iterator) {
63
+ if (result) {
64
+ if (slen-- === 0) {
65
+ result += '\n';
66
+ slen = gaussMS(wordsPerLine);
67
+ } else {
68
+ result += ' ';
69
+ }
70
+ }
71
+ result += word;
72
+ }
73
+ return result;
74
+ }
75
+ }
76
+
77
+ // decorators //
78
+ export function limit(size = [5, 10]) {
79
+ const n = typeof size === 'number' ? size : randomInt(...size);
80
+ return function *(iterator) {
81
+ let i = 0;
82
+ for (let word of iterator) {
83
+ if (i++ >= n) {
84
+ break;
85
+ }
86
+ yield word;
87
+ }
88
+ };
89
+ }
90
+
91
+ export function title({} = {}) {
92
+ return function *(iterator) {
93
+ for (let word of iterator) {
94
+ yield capitalize(word);
95
+ }
96
+ };
97
+ }
98
+
99
+ export function description({
100
+ wordsPerSentence = {
101
+ avg: 24,
102
+ std: 5,
103
+ min: 1,
104
+ },
105
+ punctuation = '.',
106
+ } = {}) {
107
+ return function *(iterator) {
108
+ let slen = 0;
109
+
110
+ yield* iterateWithLastItemSignal(iterator, function *(word, last) {
111
+ if (slen === 0) {
112
+ word = capitalize(word);
113
+ slen = gaussMS(wordsPerSentence);
114
+ }
115
+ if (--slen === 0 || last) {
116
+ word += punctuation;
117
+ }
118
+ yield word;
119
+ });
120
+ };
121
+ }
122
+
123
+ // helpers //
124
+ function capitalize(word) {
125
+ return word[0].toUpperCase() + word.substring(1);
126
+ }
127
+
128
+ // TODO: have a random variable expression
129
+ function gaussMS(args) {
130
+ if (typeof args === 'number') {
131
+ return Math.round(avg);
132
+ }
133
+ let { avg, std, min, max } = args;
134
+ if (std === undefined) {
135
+ std = avg / 4;
136
+ }
137
+ let n = gaussRandom() * std + avg;
138
+ if (min !== undefined) {
139
+ n = Math.max(min, n);
140
+ }
141
+ if (max !== undefined) {
142
+ n = Math.min(max, n);
143
+ }
144
+ return Math.round(n);
145
+ }
@@ -0,0 +1,313 @@
1
+ import { randomInt, imageUrl, shuffle, iterateWithLastItemSignal } from './utils.js';
2
+ import * as lorem from './lorem.js';
3
+ import * as languages from './languages.js';
4
+
5
+ // TODO: wild mode that generates edge cases
6
+
7
+ function extractLangFeatures(features = []) {
8
+ const languages = new Set();
9
+ const rest = [];
10
+ for (const feature of features) {
11
+ if (feature.startsWith('lang-')) {
12
+ let lang = feature.slice(5);
13
+ if (lang === 'javascript') {
14
+ lang = 'js';
15
+ }
16
+ languages.add(lang);
17
+ } else {
18
+ rest.push(feature);
19
+ }
20
+ }
21
+ return [[...languages], rest];
22
+ }
23
+
24
+ export function markdown({ sources, citation, features, blocks = [8, 12], sampling = 1 } = {}) {
25
+ let languages = [];
26
+ [languages, features] = extractLangFeatures(features);
27
+ // TODO: block features
28
+ let result = sample([
29
+ () => atxHeading({ features }),
30
+ () => paragraph({ sources, citation, features }),
31
+ ...languages.map(lang => () => fencedCodeBlock({ lang, features })),
32
+ ...(languages.length ? [] : [() => fencedCodeBlock({ features })]),
33
+ () => paragraph({ sources, citation, features }),
34
+ () => table({ features }),
35
+ () => image(),
36
+ () => paragraph({ sources, citation, features }),
37
+ () => hr(),
38
+ () => html(),
39
+ () => atxHeading({ features }),
40
+ () => paragraph({ sources, citation, features }),
41
+ () => list({ features }),
42
+ () => paragraph({ sources, citation, features }),
43
+ ], sampling).join('\n\n');
44
+ if (citation && citation.unused && citation.unused.length) {
45
+ // flush all unused citations
46
+ const indicies = [...citation.unused];
47
+ indicies.sort((a, b) => a - b);
48
+ result += indicies.map(index => _citation(citation, sources[index])).join('');
49
+ }
50
+ return result;
51
+ }
52
+
53
+ function sample(fns, sampling) {
54
+ const arr = [];
55
+ for (const fn of fns) {
56
+ if (sampling >= 1 || Math.random() < sampling) {
57
+ arr.push(fn());
58
+ }
59
+ }
60
+ return arr;
61
+ }
62
+
63
+ // leaf blocks //
64
+ export function hr() {
65
+ // wild mode: while spaces at the beginning (< 4), in between, in the end
66
+ // wild mode: no line break for '*'
67
+ return '*-_'.charAt(randomInt(0, 2)).repeat(randomInt(3, 6));
68
+ }
69
+
70
+ export function atxHeading({ features, level = [1, 6], size = [1, 8], content }) {
71
+ const words = content || lorem.lorem({ size });
72
+ return `${'#'.repeat(randomInt(...level))} ${words}`;
73
+ }
74
+
75
+ export function setextHeading({ features, level = [1, 2], size = [1, 8], content }) {
76
+ const words = content || lorem.lorem({ size });
77
+ return `${words}\n${'=-'.charAt(randomInt(...level) - 1).repeat(3)}`;
78
+ }
79
+
80
+ export function linkReferenceDefinition({ label, destination, title }) {
81
+ return `[${label}]: ${destination}${title !== undefined ? ` ${title}` : ''}`;
82
+ }
83
+
84
+ export function indentedCodeBlock({ lang, content, size }) {
85
+ content = content || codeContent({ lang, size });
86
+ return indent(4, content);
87
+ }
88
+
89
+ export function fencedCodeBlock({ lang, content, size, fenceChar = '`' }) {
90
+ if (fenceChar === 'random') {
91
+ fenceChar = '`~'.charAt(randomInt(0, 1));
92
+ }
93
+ content = content || codeContent({ lang, size });
94
+ // TODO: escape fenceChar in content
95
+ return `${fenceChar.repeat(3)}${lang || ''}\n${content}\n${fenceChar.repeat(3)}`;
96
+ }
97
+
98
+ export function paragraph({ sources, citation, features, size = [20, 50] }) {
99
+ // force all inline features
100
+ const decorates = ['description', decorateInlineFeatures()];
101
+ if (sources && citation) {
102
+ decorates.push(decorateCitation(sources, citation));
103
+ }
104
+ return lorem.lorem({ size, decorates });
105
+ }
106
+
107
+ export function table({ features, columns = [2, 4], rows = [2, 8] }) {
108
+ columns = randomInt(...columns);
109
+ rows = randomInt(...rows);
110
+ const defs = [...multiply({ size: 1 }, columns - 1), { size: [3, 8] }];
111
+ const header = lorem.lorem({ size: columns, output: 'array' });
112
+ const delimiter = defs.map(() => '---');
113
+ const body = [ header, delimiter ];
114
+ for (let i = 0; i < rows - 1; i++) {
115
+ body.push(defs.map(({ size }) => lorem.lorem({ size })));
116
+ }
117
+ return body.map(tableRow).join('\n');
118
+ }
119
+
120
+ export function image({ url, imageSize = [400, 250], ...options } = {}) {
121
+ url = url || imageUrl(imageSize);
122
+ return `![${_content(options)}](${url})`;
123
+ }
124
+
125
+ // container blocks //
126
+ export function blockquote({ features, size = [3, 5] }) {
127
+ // TODO
128
+ return _blockquote(lorem.lorem({ size }));
129
+ }
130
+
131
+ const LIST_ITEM_TYPES = ['ordered', 'bullet', 'task'];
132
+
133
+ export function list({ features, type = 'random', count = [1, 8], size = [5, 15] }) {
134
+ count = typeof count === 'number' ? count : randomInt(...count);
135
+ const t = type === 'random' ? LIST_ITEM_TYPES[Math.floor(3 * Math.random())] : type;
136
+ const items = [];
137
+ while (count > 0) {
138
+ const c = randomInt(1, count);
139
+ let content = paragraph({ features, size });
140
+ if (c > 1) {
141
+ content += `\n${list({ features, type, count: c - 1, size })}`;
142
+ }
143
+ items.push(listItem(t, content));
144
+ count -= c;
145
+ }
146
+ return items.join('\n');
147
+ }
148
+
149
+ // inline //
150
+ export function codeSpan(options) {
151
+ return `\`${_content(options)}\``;
152
+ }
153
+
154
+ export function emphasis({ level = [1, 3], options }) {
155
+ level = typeof level === 'number' ? level : randomInt(...level);
156
+ const str = '_*'.charAt(randomInt(0, 1)).repeat(level);
157
+ return `${str}${_content(options)}${str}`;
158
+ }
159
+
160
+ export function link({ url = 'https://miso.ai', ...options } = {}) {
161
+ return `[${_content(options)}](${url})`;
162
+ }
163
+
164
+ export function html({} = {}) {
165
+ return `<div>${svg()}</div>`;
166
+ }
167
+
168
+ function svg() {
169
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calculator" viewBox="0 0 16 16"><path d="M12 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/><path d="M4 2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5zm0 4a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm3-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm3-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z"/></svg>`;
170
+ }
171
+
172
+ // TODO: ref link
173
+ // TODO: autolink
174
+ // TODO: hard line break
175
+
176
+ const INLINE_FEATURES = {
177
+ 'code-span': () => ['`', '`'],
178
+ 'emphasis-1': () => multiply(_emphasisAdfix(1), 2),
179
+ 'emphasis-2': () => multiply(_emphasisAdfix(2), 2),
180
+ 'emphasis-3': () => multiply(_emphasisAdfix(3), 2),
181
+ 'strikethrough': () => ['~', '~'],
182
+ 'link': ({ url = 'https://miso.ai' } = {}) => [`[`, `](${url})`],
183
+ };
184
+
185
+ function _emphasisAdfix(level = [1, 3]) {
186
+ level = typeof level === 'number' ? level : randomInt(...level);
187
+ return '_*'.charAt(randomInt(0, 1)).repeat(level);
188
+ }
189
+
190
+ const INLINE_FEATURE_LIST = Object.keys(INLINE_FEATURES);
191
+ const INLINE_FEATURE_SET = new Set(INLINE_FEATURE_LIST);
192
+
193
+ // decorator //
194
+ export function decorateInlineFeatures({ features = INLINE_FEATURE_LIST, size = [1, 3], rest = [0, 8] } = {}) {
195
+ features = features.filter(f => INLINE_FEATURE_SET.has(f));
196
+
197
+ const unused = shuffle([...features]);
198
+ let unusedCursor = unused.length - 1;
199
+
200
+ const rollRest = () => typeof rest === 'number' ? rest : randomInt(...rest);
201
+ const rollFeatureSize = () => typeof size === 'number' ? size : randomInt(...size);
202
+ const rollFeatureType = () => unusedCursor >= 0 ? unused[unusedCursor--] : features[randomInt(0, features.length - 1)];
203
+
204
+ return function *(iterator) {
205
+ let count = rollRest();
206
+ let suffix;
207
+
208
+ yield *iterateWithLastItemSignal(iterator, function *(word, last) {
209
+ // prefix if necessary
210
+ if (!suffix && count === 0) {
211
+ const [prefix, s] = INLINE_FEATURES[rollFeatureType()]();
212
+ word = `${prefix}${word}`;
213
+ count = rollFeatureSize();
214
+ suffix = s;
215
+ }
216
+
217
+ // consume word count
218
+ count--;
219
+
220
+ // suffix if necessary
221
+ if (suffix) {
222
+ const len = word.length;
223
+ const lastChar = word.charAt(len - 1);
224
+ if (isPunctuation(lastChar)) {
225
+ word = `${word.substring(0, len - 1)}${suffix}${lastChar}`;
226
+ suffix = undefined;
227
+ count = rollRest();
228
+ } else if (last || count === 0) {
229
+ word = `${word}${suffix}`;
230
+ suffix = undefined;
231
+ count = rollRest();
232
+ }
233
+ }
234
+
235
+ // output
236
+ yield word;
237
+ });
238
+ };
239
+ }
240
+
241
+ export function decorateCitation(sources, { density = 0.667, unused, ...options }) {
242
+ const sourceLength = sources.length;
243
+ const rollIndex = () => unused && unused.length ? unused.pop() : randomInt(0, sourceLength - 1);
244
+
245
+ return function *(iterator) {
246
+ for (const word of iterator) {
247
+ // not ended with alphabet or number -> last word in sentence
248
+ if (word.charAt(word.length - 1) === '.' && Math.random() < density) {
249
+ yield `${word}${_citation(options, sources[rollIndex()])}`;
250
+ } else {
251
+ yield word;
252
+ }
253
+ }
254
+ };
255
+ }
256
+
257
+ // helper //
258
+ function _content({ size = [1, 3], content } = {}) {
259
+ return content || lorem.lorem({ size });
260
+ }
261
+
262
+ function _blockquote(content) {
263
+ return content.split('\n').map(line => `> ${line}`).join('\n');
264
+ }
265
+
266
+ export function indent(size, content) {
267
+ return content.split('\n').map(line => ' '.repeat(size) + line).join('\n');
268
+ }
269
+
270
+ export function listItem(type, content) {
271
+ const [firstLine, restLines] = content.split('\n', 2);
272
+ const result = `${listItemPrefix(type)} ${firstLine}`;
273
+ const indentSize = type === 'ordered' ? 3 : 2;
274
+ return !restLines ? result : result + `\n${indent(indentSize, restLines)}`;
275
+ }
276
+
277
+ function listItemPrefix(type, checked = 'random') {
278
+ switch (type) {
279
+ case 'ordered':
280
+ return '1.';
281
+ case 'bullet':
282
+ return '-';
283
+ case 'task':
284
+ const mark = (checked === 'random' ? Math.random() < 0.5 : !!checked) ? 'x' : ' ';
285
+ return `- [${mark}]`;
286
+ default:
287
+ throw new Error(`unknown list item type: ${type}`);
288
+ }
289
+ }
290
+
291
+ function codeContent({ lang, size = [10, 30] }) {
292
+ return lang && languages[lang] ? languages[lang]() : lorem.lorem({ output: 'multiline', size });
293
+ }
294
+
295
+ function tableRow(cells) {
296
+ return `| ${cells.join(' | ')} |`;
297
+ }
298
+
299
+ function multiply(obj, i) {
300
+ const arr = [];
301
+ for (let j = 0; j < i; j++) {
302
+ arr.push(typeof obj === 'function' ? obj() : obj);
303
+ }
304
+ return arr;
305
+ }
306
+
307
+ function isPunctuation(char) {
308
+ return char === '.' || char === ',' || char === '!' || char === '?' || char === ':' || char === ';';
309
+ }
310
+
311
+ function _citation({ link, start, end }, { index, url }) {
312
+ return link ? `[${start}${index}${end}](${url})` : `${start}${index}${end}`;
313
+ }
package/lib/utils.js ADDED
@@ -0,0 +1,73 @@
1
+ export function uuid() {
2
+ return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, a => (a ^ Math.random() * 16 >> a / 4).toString(16));
3
+ }
4
+
5
+ export function randomInt(min, max) {
6
+ return max == null || (max <= min) ? min : (min + Math.floor(Math.random() * (max - min + 1)));
7
+ }
8
+
9
+ // TODO: pass in size
10
+ export function repeat(fn, range) {
11
+ const n = randomInt(...range);
12
+ const result = [];
13
+ for (let i = 0; i < n; i++) {
14
+ result.push(fn());
15
+ }
16
+ return result;
17
+ }
18
+
19
+ export function id() {
20
+ return Math.random().toString(36).substring(2, 10);
21
+ }
22
+
23
+ export function shuffle(array) {
24
+ for (let i = array.length - 1; i > 0; i--) {
25
+ const j = Math.floor(Math.random() * (i + 1));
26
+ [array[i], array[j]] = [array[j], array[i]];
27
+ }
28
+ return array;
29
+ }
30
+
31
+ export function imageUrl(size) {
32
+ const seed = Math.floor(Math.random() * 1000);
33
+ const sizePath = Array.isArray(size) ? size.length > 1 ? `${size[0]}/${size[1]}` : `${size[0]}` : `${size}`;
34
+ return `https://picsum.photos/seed/${seed}/${sizePath}`;
35
+ }
36
+
37
+ export function formatDatetime(timestamp) {
38
+ const str = new Date(timestamp).toISOString();
39
+ return str.endsWith('Z') ? str.slice(0, -1) : str;
40
+ }
41
+
42
+ export function sample(size, sampling) {
43
+ return sampling !== undefined ? Math.ceil(size * sampling) : size;
44
+ }
45
+
46
+ export function gaussRandom() {
47
+ return uniformRandom() + uniformRandom() + uniformRandom();
48
+ }
49
+
50
+ export function rollLatency(min, max) {
51
+ return (min + max) / 2 + gaussRandom() * (max - min) / 6;
52
+ }
53
+
54
+ function uniformRandom() {
55
+ return Math.random() * 2 - 1;
56
+ }
57
+
58
+ export function *iterateWithLastItemSignal(iterator, fn) {
59
+ let last;
60
+ for (const item of iterator) {
61
+ if (last) {
62
+ yield* fn(last);
63
+ }
64
+ last = item;
65
+ }
66
+ if (last) {
67
+ yield* fn(last, true);
68
+ }
69
+ }
70
+
71
+ export function excludeHtml({ html, ...rest }) {
72
+ return rest;
73
+ }
package/lib/words.js ADDED
@@ -0,0 +1,182 @@
1
+
2
+ // genereated by bin/data.js
3
+ export default [
4
+ "lorem",
5
+ "ipsum",
6
+ "dolor",
7
+ "sit",
8
+ "amet",
9
+ "consectetur",
10
+ "adipiscing",
11
+ "elit",
12
+ "curabitur",
13
+ "vel",
14
+ "hendrerit",
15
+ "libero",
16
+ "eleifend",
17
+ "blandit",
18
+ "nunc",
19
+ "ornare",
20
+ "odio",
21
+ "ut",
22
+ "orci",
23
+ "gravida",
24
+ "imperdiet",
25
+ "nullam",
26
+ "purus",
27
+ "lacinia",
28
+ "a",
29
+ "pretium",
30
+ "quis",
31
+ "congue",
32
+ "praesent",
33
+ "sagittis",
34
+ "laoreet",
35
+ "auctor",
36
+ "mauris",
37
+ "non",
38
+ "velit",
39
+ "eros",
40
+ "dictum",
41
+ "proin",
42
+ "accumsan",
43
+ "sapien",
44
+ "nec",
45
+ "massa",
46
+ "volutpat",
47
+ "venenatis",
48
+ "sed",
49
+ "eu",
50
+ "molestie",
51
+ "lacus",
52
+ "quisque",
53
+ "porttitor",
54
+ "ligula",
55
+ "dui",
56
+ "mollis",
57
+ "tempus",
58
+ "at",
59
+ "magna",
60
+ "vestibulum",
61
+ "turpis",
62
+ "ac",
63
+ "diam",
64
+ "tincidunt",
65
+ "id",
66
+ "condimentum",
67
+ "enim",
68
+ "sodales",
69
+ "in",
70
+ "hac",
71
+ "habitasse",
72
+ "platea",
73
+ "dictumst",
74
+ "aenean",
75
+ "neque",
76
+ "fusce",
77
+ "augue",
78
+ "leo",
79
+ "eget",
80
+ "semper",
81
+ "mattis",
82
+ "tortor",
83
+ "scelerisque",
84
+ "nulla",
85
+ "interdum",
86
+ "tellus",
87
+ "malesuada",
88
+ "rhoncus",
89
+ "porta",
90
+ "sem",
91
+ "aliquet",
92
+ "et",
93
+ "nam",
94
+ "suspendisse",
95
+ "potenti",
96
+ "vivamus",
97
+ "luctus",
98
+ "fringilla",
99
+ "erat",
100
+ "donec",
101
+ "justo",
102
+ "vehicula",
103
+ "ultricies",
104
+ "varius",
105
+ "ante",
106
+ "primis",
107
+ "faucibus",
108
+ "ultrices",
109
+ "posuere",
110
+ "cubilia",
111
+ "curae",
112
+ "etiam",
113
+ "cursus",
114
+ "aliquam",
115
+ "quam",
116
+ "dapibus",
117
+ "nisl",
118
+ "feugiat",
119
+ "egestas",
120
+ "class",
121
+ "aptent",
122
+ "taciti",
123
+ "sociosqu",
124
+ "ad",
125
+ "litora",
126
+ "torquent",
127
+ "per",
128
+ "conubia",
129
+ "nostra",
130
+ "inceptos",
131
+ "himenaeos",
132
+ "phasellus",
133
+ "nibh",
134
+ "pulvinar",
135
+ "vitae",
136
+ "urna",
137
+ "iaculis",
138
+ "lobortis",
139
+ "nisi",
140
+ "viverra",
141
+ "arcu",
142
+ "morbi",
143
+ "pellentesque",
144
+ "metus",
145
+ "commodo",
146
+ "ut",
147
+ "facilisis",
148
+ "felis",
149
+ "tristique",
150
+ "ullamcorper",
151
+ "placerat",
152
+ "aenean",
153
+ "convallis",
154
+ "sollicitudin",
155
+ "integer",
156
+ "rutrum",
157
+ "duis",
158
+ "est",
159
+ "etiam",
160
+ "bibendum",
161
+ "donec",
162
+ "pharetra",
163
+ "vulputate",
164
+ "maecenas",
165
+ "mi",
166
+ "fermentum",
167
+ "consequat",
168
+ "suscipit",
169
+ "aliquam",
170
+ "habitant",
171
+ "senectus",
172
+ "netus",
173
+ "fames",
174
+ "quisque",
175
+ "euismod",
176
+ "curabitur",
177
+ "lectus",
178
+ "elementum",
179
+ "tempor",
180
+ "risus",
181
+ "cras"
182
+ ];
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@miso.ai/lorem",
3
+ "description": "Lorem support for doggoganger",
4
+ "type": "module",
5
+ "main": "lib/index.js",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "generate:data": "node ./bin/data.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/MisoAI/doggoganger.git"
15
+ },
16
+ "homepage": "https://github.com/MisoAI/doggoganger/",
17
+ "license": "MIT",
18
+ "contributors": [
19
+ "simonpai <simon.pai@askmiso.com>"
20
+ ],
21
+ "bugs": {
22
+ "url": "https://github.com/MisoAI/doggoganger/issues"
23
+ },
24
+ "devDependencies": {
25
+ "js-yaml": "^4.1.0"
26
+ },
27
+ "version": "0.0.1"
28
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@miso.ai/lorem",
3
+ "description": "Lorem support for doggoganger",
4
+ "type": "module",
5
+ "main": "lib/index.js",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "generate:data": "node ./bin/data.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/MisoAI/doggoganger.git"
15
+ },
16
+ "homepage": "https://github.com/MisoAI/doggoganger/",
17
+ "license": "MIT",
18
+ "contributors": [
19
+ "simonpai <simon.pai@askmiso.com>"
20
+ ],
21
+ "bugs": {
22
+ "url": "https://github.com/MisoAI/doggoganger/issues"
23
+ },
24
+ "devDependencies": {
25
+ "js-yaml": "^4.1.0"
26
+ }
27
+ }