@miso.ai/doggoganger-api 0.0.1 → 0.10.0-beta.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/lib/api/api.js ADDED
@@ -0,0 +1,21 @@
1
+ import { Ask } from './ask.js';
2
+ import { Search } from './search.js';
3
+ import { Recommendation } from './recommendation.js';
4
+ import { Interactions } from './interactions.js';
5
+ import { Products } from './products.js';
6
+
7
+ export function api(options) {
8
+ return new Api(options);
9
+ }
10
+
11
+ export class Api {
12
+
13
+ constructor(options = {}) {
14
+ this.ask = new Ask(options);
15
+ this.search = new Search(options);
16
+ this.recommendation = new Recommendation(options);
17
+ this.interactions = new Interactions(options);
18
+ this.products = new Products(options);
19
+ }
20
+
21
+ }
package/lib/api/ask.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { misoData } from '../data/index.js';
1
2
  import { trimObj } from '../utils.js';
2
- import { answer, searchResults, questions, completions, utils } from '../data/index.js';
3
3
 
4
4
  const CPS = 100;
5
5
  const ITEMS_LOADING_TIME = 3;
@@ -25,40 +25,43 @@ const STAGES = [
25
25
  const MODE_QUESTION = 0;
26
26
  const MODE_SEARCH = 1;
27
27
 
28
- export default class Ask {
28
+ export class Ask {
29
29
 
30
30
  constructor(options = {}) {
31
31
  this._options = options;
32
32
  this._answers = new Map();
33
33
  }
34
34
 
35
- questions(payload, options = {}) {
36
- const answer = this._createAnswer(MODE_QUESTION, payload, options);
35
+ questions(payload, { seed, ...options } = {}) {
36
+ const data = misoData({ seed });
37
+ const answer = this._createAnswer(data, MODE_QUESTION, payload, options);
37
38
  const { question_id } = answer;
38
39
  return { question_id };
39
40
  }
40
41
 
41
- search(payload, options = {}) {
42
- const miso_id = utils.uuid();
43
- const result = { miso_id, ...searchResults(payload) };
42
+ search(payload, { seed, ...options } = {}) {
43
+ const data = misoData({ seed });
44
+ const miso_id = data._lorem.prng.uuid();
45
+ const result = { miso_id, ...data.searchResults(payload) };
44
46
  if (payload.answer === undefined || payload.answer) {
45
- result.question_id = this._createAnswer(MODE_SEARCH, payload, options).question_id;
47
+ result.question_id = this._createAnswer(data, MODE_SEARCH, payload, options).question_id;
46
48
  }
47
49
  return result;
48
50
  }
49
51
 
50
- autocomplete({ q, completion_fields = ['title'], rows = 5 }) {
52
+ autocomplete({ q, completion_fields = ['title'], rows = 5, ...rest }, { seed } = {}) {
53
+ const data = misoData({ seed });
51
54
  return {
52
- completions: completions({ q, completion_fields, rows }),
55
+ completions: data.completions({ q, completion_fields, rows }),
53
56
  };
54
57
  }
55
58
 
56
- search_autocomplete(args) {
57
- return this.autocomplete(args);
59
+ search_autocomplete(args, options) {
60
+ return this.autocomplete(args, options);
58
61
  }
59
62
 
60
- _createAnswer(mode, payload, options = {}) {
61
- const answer = new Answer(mode, payload, { ...this._options, ...options });
63
+ _createAnswer(data, mode, payload, options = {}) {
64
+ const answer = new Answer(data, mode, payload, { ...this._options, ...options });
62
65
  this._answers.set(answer.question_id, answer);
63
66
  return answer;
64
67
  }
@@ -73,27 +76,28 @@ export default class Ask {
73
76
  return answer.get();
74
77
  }
75
78
 
76
- related_questions(payload) {
77
- const miso_id = utils.uuid();
79
+ related_questions(payload, { seed } = {}) {
80
+ const data = misoData({ seed });
81
+ const miso_id = data._lorem.prng.uuid();
78
82
  return {
79
- related_questions: [...questions(payload)],
83
+ related_questions: data.questions(payload),
80
84
  miso_id,
81
85
  };
82
86
  }
83
87
 
84
- trending_questions(payload) {
85
- return this.related_questions(payload);
88
+ trending_questions(payload, options) {
89
+ return this.related_questions(payload, options);
86
90
  }
87
91
 
88
92
  }
89
93
 
90
94
  class Answer {
91
95
 
92
- constructor(mode, payload, { answerFormat, answerSampling, answerLanguages, ...options } = {}) {
96
+ constructor(data, mode, payload, { answerFormat, answerSampling, answerLanguages, ...options } = {}) {
93
97
  this._mode = mode;
94
98
  this._options = Object.freeze(options);
95
99
  const timestamp = this.timestamp = Date.now();
96
- this._data = answer({ ...payload, timestamp }, { answerFormat, answerSampling, answerLanguages });
100
+ this._data = data.answer({ ...payload, timestamp }, { answerFormat, answerSampling, answerLanguages });
97
101
  }
98
102
 
99
103
  get question_id() {
package/lib/api/index.js CHANGED
@@ -1,17 +1 @@
1
- import Ask from './ask.js';
2
- import Search from './search.js';
3
- import Recommendation from './recommendation.js';
4
- import Interactions from './interactions.js';
5
- import Products from './products.js';
6
-
7
- export default class Api {
8
-
9
- constructor(options) {
10
- this.ask = new Ask(options);
11
- this.search = new Search(options);
12
- this.recommendation = new Recommendation(options);
13
- this.interactions = new Interactions(options);
14
- this.products = new Products(options);
15
- }
16
-
17
- }
1
+ export * from './api.js';
@@ -1,10 +1,10 @@
1
- export default class Interactions {
1
+ export class Interactions {
2
2
 
3
3
  constructor(options) {
4
4
  this._options = options;
5
5
  }
6
6
 
7
- upload(records) {
7
+ upload(records, { seed } = {}) {
8
8
  return [];
9
9
  }
10
10
 
@@ -1,18 +1,18 @@
1
- export default class Products {
1
+ export class Products {
2
2
 
3
3
  constructor(options) {
4
4
  this._options = options;
5
5
  }
6
6
 
7
- upload(records) {
7
+ upload(records, { seed } = {}) {
8
8
  return [];
9
9
  }
10
10
 
11
- batchDelete(ids) {
11
+ batchDelete(ids, { seed } = {}) {
12
12
  return [];
13
13
  }
14
14
 
15
- ids() {
15
+ ids({ seed } = {}) {
16
16
  const ids = [];
17
17
  for (let i = 0; i < 5000; i++) {
18
18
  ids.push(mockProductId(i));
@@ -1,20 +1,22 @@
1
- import { products } from '../data/index.js';
1
+ import { misoData } from '../data/index.js';
2
2
 
3
- export default class Recommendation {
3
+ export class Recommendation {
4
4
 
5
5
  constructor(options) {
6
6
  this._options = options;
7
7
  }
8
8
 
9
- user_to_products({ rows = 5 }) {
9
+ user_to_products({ rows = 5, ...rest }, { seed } = {}) {
10
+ const data = misoData({ seed });
10
11
  return {
11
- products: [...products({ rows })],
12
+ products: data.products({ rows }),
12
13
  };
13
14
  }
14
15
 
15
- product_to_products({ rows = 5 }) {
16
+ product_to_products({ rows = 5, ...rest }, { seed } = {}) {
17
+ const data = misoData({ seed });
16
18
  return {
17
- products: [...products({ rows })],
19
+ products: data.products({ rows }),
18
20
  };
19
21
  }
20
22
 
package/lib/api/search.js CHANGED
@@ -1,20 +1,22 @@
1
- import { products, completions } from '../data/index.js';
1
+ import { misoData } from '../data/index.js';
2
2
 
3
- export default class Search {
3
+ export class Search {
4
4
 
5
5
  constructor(options) {
6
6
  this._options = options;
7
7
  }
8
8
 
9
- search({ rows = 5 }) {
9
+ search({ rows = 5, ...rest }, { seed } = {}) {
10
+ const data = misoData({ seed });
10
11
  return {
11
- products: [...products({ rows })],
12
+ products: data.products({ rows }),
12
13
  };
13
14
  }
14
15
 
15
- autocomplete({ q, completion_fields = ['title'], rows = 5 }) {
16
+ autocomplete({ q, completion_fields = ['title'], rows = 5, ...rest }, { seed } = {}) {
17
+ const data = misoData({ seed });
16
18
  return {
17
- completions: completions({ q, completion_fields, rows }),
19
+ completions: data.completions({ q, completion_fields, rows }),
18
20
  };
19
21
  }
20
22
 
@@ -1,69 +1,74 @@
1
- import { fields, utils } from '@miso.ai/lorem';
2
- import { articles } from './articles.js';
3
- import { images as _images } from './images.js';
4
- import { questions } from './questions.js';
5
- import { facets as generateFacetFields } from './facets.js';
1
+ import { formatDatetime, sample, excludeHtml } from '../utils.js';
6
2
 
7
- const { randomInt, formatDatetime, sample, uuid, shuffle, excludeHtml } = utils;
3
+ export class Answers {
8
4
 
9
- export function answer({
10
- _ctrl = {},
11
- question,
12
- parent_question_id,
13
- fl = ['cover_image', 'url'],
14
- source_fl = ['cover_image', 'url'],
15
- related_resource_fl = ['cover_image', 'url'],
16
- cite_link = false,
17
- cite_start = '[',
18
- cite_end = ']',
19
- rows = 10,
20
- facets,
21
- timestamp = Date.now(),
22
- _meta: {
23
- page = 0,
24
- } = {},
25
- }, { answerFormat = 'markdown', answerSampling, answerLanguages = [] } = {}) {
5
+ constructor(data) {
6
+ this._data = data;
7
+ }
26
8
 
27
- const question_id = uuid();
28
- const datetime = formatDatetime(timestamp);
9
+ _answer({
10
+ _ctrl = {},
11
+ question,
12
+ parent_question_id,
13
+ fl = ['cover_image', 'url'],
14
+ source_fl = ['cover_image', 'url'],
15
+ related_resource_fl = ['cover_image', 'url'],
16
+ cite_link = false,
17
+ cite_start = '[',
18
+ cite_end = ']',
19
+ rows = 10,
20
+ facets,
21
+ timestamp = Date.UTC(2026, 0, 1),
22
+ _meta: {
23
+ page = 0,
24
+ } = {},
25
+ }, { answerFormat = 'markdown', answerSampling, answerLanguages = [] } = {}) {
29
26
 
30
- const sampling = answerSampling !== undefined ? Math.max(0, Math.min(1, answerSampling)) : undefined;
31
- const features = answerLanguages.length ? answerLanguages.map(language => `lang-${language}`) : undefined;
27
+ const data = this._data;
28
+ const { fields, prng, utils } = data._lorem;
32
29
 
33
- const related_resources = [...articles({ rows: sampleRandomInt(6, 8, sampling), fl: related_resource_fl })].map(excludeHtml);
34
- const images = [..._images({ rows: sampleRandomInt(2, 12, sampling) })];
35
- const sources = [...articles({ rows: sampleRandomInt(4, 6, sampling), fl: source_fl })].map(excludeHtml);
30
+ const question_id = prng.uuid();
31
+ const datetime = formatDatetime(timestamp);
36
32
 
37
- const total = _ctrl.total !== undefined ? _ctrl.total : randomInt(1000, 10000);
38
- const products = [...articles({ rows: Math.min(total - page * rows, rows), fl })].map(excludeHtml);
33
+ const sampling = answerSampling !== undefined ? Math.max(0, Math.min(1, answerSampling)) : undefined;
34
+ const features = answerLanguages.length ? answerLanguages.map(language => `lang-${language}`) : undefined;
39
35
 
40
- const facet_counts = facets ? { facet_fields: generateFacetFields({ facets, _ctrl }) } : undefined;
36
+ const related_resources = data.articles({ rows: this._sampleRandomInt(6, 8, sampling), fl: related_resource_fl }).map(excludeHtml);
37
+ const images = data.images({ rows: this._sampleRandomInt(2, 12, sampling) });
38
+ const sources = data.articles({ rows: this._sampleRandomInt(4, 6, sampling), fl: source_fl }).map(excludeHtml);
41
39
 
42
- const citation = {
43
- link: cite_link !== '0' && !!cite_link,
44
- start: cite_start,
45
- end: cite_end,
46
- unused: shuffle([...Array(sources.length).keys()]),
47
- };
48
- const answer = fields.answer({ sources, citation, format: answerFormat, sampling, features });
49
- const followup_questions = [...questions({ rows: sampleRandomInt(3, 6) })];
40
+ const total = _ctrl.total !== undefined ? _ctrl.total : prng.randomInt(1000, 10000);
41
+ const products = data.articles({ rows: Math.min(total - page * rows, rows), fl }).map(excludeHtml);
50
42
 
51
- return {
52
- question,
53
- question_id,
54
- ...(parent_question_id && { parent_question_id }),
55
- datetime,
56
- answer,
57
- images,
58
- sources,
59
- products,
60
- total,
61
- facet_counts,
62
- related_resources,
63
- followup_questions,
64
- };
65
- }
43
+ const facet_counts = facets ? { facet_fields: data.facets({ facets, _ctrl }) } : undefined;
44
+
45
+ const citation = {
46
+ link: cite_link !== '0' && !!cite_link,
47
+ start: cite_start,
48
+ end: cite_end,
49
+ unused: utils.shuffle([...Array(sources.length).keys()]),
50
+ };
51
+ const answer = fields.answer({ sources, citation, format: answerFormat, sampling, features });
52
+ const followup_questions = data.questions({ rows: this._sampleRandomInt(3, 6) });
53
+
54
+ return {
55
+ question,
56
+ question_id,
57
+ ...(parent_question_id && { parent_question_id }),
58
+ datetime,
59
+ answer,
60
+ images,
61
+ sources,
62
+ products,
63
+ total,
64
+ facet_counts,
65
+ related_resources,
66
+ followup_questions,
67
+ };
68
+ }
69
+
70
+ _sampleRandomInt(min, max, sampling) {
71
+ return this._data._lorem.prng.randomInt(sample(min, sampling), sample(max, sampling));
72
+ }
66
73
 
67
- function sampleRandomInt(min, max, sampling) {
68
- return randomInt(sample(min, sampling), sample(max, sampling));
69
74
  }
@@ -1,5 +1,3 @@
1
- import { fields, utils } from '@miso.ai/lorem';
2
-
3
1
  const FIELDS = new Set([
4
2
  'cover_image',
5
3
  'url',
@@ -8,43 +6,49 @@ const FIELDS = new Set([
8
6
  'published_at',
9
7
  ]);
10
8
 
11
- export function *articles({ rows, ...options } = {}) {
12
- for (let i = 0; i < rows; i++) {
13
- yield article({ ...options, index: i });
9
+ export class Articles {
10
+
11
+ constructor(data) {
12
+ this._lorem = data._lorem;
14
13
  }
15
- }
16
14
 
17
- function article({ html, fl = [] } = {}) {
18
- const id = utils.id();
19
-
20
- const article = {
21
- product_id: id,
22
- authors: fields.authors(),
23
- categories: [],
24
- tags: fields.tags(),
25
- title: fields.title({ size: [4, 10] }),
26
- snippet: fields.description({ size: [20, 40] }),
27
- html: fields.html(html),
28
- };
29
-
30
- for (const field of fl) {
31
- if (FIELDS.has(field)) {
32
- article[field] = property(article, field);
15
+ _article({ html, fl = [] } = {}) {
16
+ const { fields, prng } = this._lorem;
17
+
18
+ const id = prng.shortId();
19
+
20
+ const article = {
21
+ product_id: id,
22
+ authors: fields.authors(),
23
+ categories: [],
24
+ tags: fields.tags(),
25
+ title: fields.title({ size: [4, 10] }),
26
+ snippet: fields.description({ size: [20, 40] }),
27
+ html: fields.html(html),
28
+ };
29
+
30
+ for (const field of fl) {
31
+ if (FIELDS.has(field)) {
32
+ article[field] = this._property(article, field);
33
+ }
33
34
  }
35
+
36
+ return article;
34
37
  }
35
38
 
36
- return article;
37
- }
39
+ _property({ product_id }, field) {
40
+ const { fields } = this._lorem;
38
41
 
39
- function property({ product_id }, field) {
40
- switch (field) {
41
- case 'cover_image':
42
- return fields.image();
43
- case 'url':
44
- return `/products/${product_id}`;
45
- case 'created_at':
46
- case 'updated_at':
47
- case 'published_at':
48
- return fields.date();
42
+ switch (field) {
43
+ case 'cover_image':
44
+ return fields.image();
45
+ case 'url':
46
+ return `/products/${product_id}`;
47
+ case 'created_at':
48
+ case 'updated_at':
49
+ case 'published_at':
50
+ return fields.date();
51
+ }
49
52
  }
53
+
50
54
  }
@@ -1,37 +1,45 @@
1
- import { fields, utils } from '@miso.ai/lorem';
1
+ export class Completions {
2
2
 
3
- export function completions({ q, completion_fields, rows } = {}) {
4
- let index = 0;
5
- const result = {};
6
- for (const field of completion_fields) {
7
- result[field] = completionsForField(field, q, index, rows);
8
- index += rows;
3
+ constructor(data) {
4
+ this._lorem = data._lorem;
9
5
  }
10
- return result;
11
- }
12
6
 
13
- function completionsForField(field, q, index, rows) {
14
- const completions = [];
15
- for (let i = 0; i < rows; i++) {
16
- completions.push(completion(field, q, index));
7
+ _completions({ q, completion_fields, rows } = {}) {
8
+ let index = 0;
9
+ const result = {};
10
+ for (const field of completion_fields) {
11
+ result[field] = this._completionsForField(field, q, index, rows);
12
+ index += rows;
13
+ }
14
+ return result;
17
15
  }
18
- return completions;
19
- }
20
16
 
21
- function completion(field, q, index) {
22
- const text = fields.title();
23
- const i = utils.randomInt(0, text.length);
24
- const prefix = text.substring(0, i);
25
- const suffix = text.substring(i);
26
- return {
27
- text: `${prefix}${q}${suffix}`,
28
- text_with_markups: `${marked(prefix)}${q}${marked(suffix)}`,
29
- text_with_inverted_markups: `${prefix}${marked(q)}${suffix}`,
30
- _field: field,
31
- _index: index,
32
- };
33
- }
17
+ _completionsForField(field, q, index, rows) {
18
+ const completions = [];
19
+ for (let i = 0; i < rows; i++) {
20
+ completions.push(this._completion(field, q, index));
21
+ }
22
+ return completions;
23
+ }
24
+
25
+ _completion(field, q, index) {
26
+ const { fields, prng } = this._lorem;
27
+
28
+ const text = fields.title();
29
+ const i = prng.randomInt(0, text.length);
30
+ const prefix = text.substring(0, i);
31
+ const suffix = text.substring(i);
32
+ return {
33
+ text: `${prefix}${q}${suffix}`,
34
+ text_with_markups: `${this._marked(prefix)}${q}${this._marked(suffix)}`,
35
+ text_with_inverted_markups: `${prefix}${this._marked(q)}${suffix}`,
36
+ _field: field,
37
+ _index: index,
38
+ };
39
+ }
40
+
41
+ _marked(text) {
42
+ return text.length > 0 ? `<mark>${text}</mark>` : '';
43
+ }
34
44
 
35
- function marked(text) {
36
- return text.length > 0 ? `<mark>${text}</mark>` : '';
37
45
  }
@@ -0,0 +1,89 @@
1
+ import { lorem } from '@miso.ai/lorem';
2
+ import { Products } from './products.js';
3
+ import { Articles } from './articles.js';
4
+ import { Questions } from './questions.js';
5
+ import { Images } from './images.js';
6
+ import { Completions } from './completions.js';
7
+ import { Facets } from './facets.js';
8
+ import { Search } from './search.js';
9
+ import { Answers } from './answers.js';
10
+
11
+ export function misoData(options) {
12
+ return new MisoData(options);
13
+ }
14
+
15
+ class MisoData {
16
+
17
+ constructor(options) {
18
+ this._lorem = lorem(options);
19
+
20
+ // Initialize in dependency order
21
+ this._articles = new Articles(this);
22
+ this._products = new Products(this);
23
+ this._questions = new Questions(this);
24
+ this._images = new Images(this);
25
+ this._completions = new Completions(this);
26
+ this._facets = new Facets(this);
27
+
28
+ // These depend on the above
29
+ this._search = new Search(this);
30
+ this._answers = new Answers(this);
31
+ }
32
+
33
+ // Articles
34
+ articles({ rows, ...options } = {}) {
35
+ const results = [];
36
+ for (let i = 0; i < rows; i++) {
37
+ results.push(this._articles._article({ ...options, index: i }));
38
+ }
39
+ return results;
40
+ }
41
+
42
+ // Products
43
+ products({ rows, ...options } = {}) {
44
+ const results = [];
45
+ for (let i = 0; i < rows; i++) {
46
+ results.push(this._products._product({ ...options, index: i }));
47
+ }
48
+ return results;
49
+ }
50
+
51
+ // Questions
52
+ questions({ rows = 5, ...options } = {}) {
53
+ const results = [];
54
+ for (let i = 0; i < rows; i++) {
55
+ results.push(this._questions._question({ ...options, index: i }));
56
+ }
57
+ return results;
58
+ }
59
+
60
+ // Images
61
+ images({ rows, ...options } = {}) {
62
+ const results = [];
63
+ for (let i = 0; i < rows; i++) {
64
+ results.push(this._images._image({ ...options, index: i }));
65
+ }
66
+ return results;
67
+ }
68
+
69
+ // Completions
70
+ completions(options) {
71
+ return this._completions._completions(options);
72
+ }
73
+
74
+ // Facets
75
+ facets(options) {
76
+ return this._facets._facets(options);
77
+ }
78
+
79
+ // Search
80
+ searchResults(options) {
81
+ return this._search._searchResults(options);
82
+ }
83
+
84
+ // Answers
85
+ answer(payload, options) {
86
+ return this._answers._answer(payload, options);
87
+ }
88
+
89
+ }
@@ -1,5 +1,3 @@
1
- import { fields } from '@miso.ai/lorem';
2
-
3
1
  const DEFAULT_FACET_SIZE = 5;
4
2
  const MAX_FACET_SIZE = 10;
5
3
  const MIN_FACET_SIZE = 1;
@@ -7,52 +5,60 @@ const MIN_FACET_SIZE = 1;
7
5
  const HIGHEST_FACET_COUNT_RATIO_RANGE = [1000, 5000];
8
6
  const NEXT_FACET_COUNT_RATIO_RANGE = [0.75, 0.25];
9
7
 
10
- export function facets({ facets, ...options } = {}) {
11
- const results = {};
12
- for (let facet of facets) {
13
- if (typeof facet === 'string') {
14
- facet = { field: facet };
8
+ export class Facets {
9
+
10
+ constructor(data) {
11
+ this._lorem = data._lorem;
12
+ }
13
+
14
+ _facets({ facets, ...options } = {}) {
15
+ const results = {};
16
+ for (let facet of facets) {
17
+ if (typeof facet === 'string') {
18
+ facet = { field: facet };
19
+ }
20
+ results[facet.field] = this._facetCountList(facet, options);
15
21
  }
16
- results[facet.field] = facetCountList(facet, options);
22
+ return results;
17
23
  }
18
- return results;
19
- }
20
24
 
21
- function facetCountList(facet, { _ctrl = {}, ...options } = {}) {
22
- let { field, size = DEFAULT_FACET_SIZE } = facet;
23
- size = _ctrl.total === 0 ? 0 : Math.max(MIN_FACET_SIZE, Math.min(size, MAX_FACET_SIZE));
24
- let count = highestFacetCountValue(field, options);
25
+ _facetCountList(facet, { _ctrl = {}, ...options } = {}) {
26
+ let { field, size = DEFAULT_FACET_SIZE } = facet;
27
+ size = _ctrl.total === 0 ? 0 : Math.max(MIN_FACET_SIZE, Math.min(size, MAX_FACET_SIZE));
28
+ let count = this._highestFacetCountValue(field, options);
25
29
 
26
- const usedTerms = new Set();
27
- const results = [];
28
- for (let i = 0; i < size; i++) {
29
- results.push([ getTerm(field, usedTerms), count ]);
30
- count = nextFacetCountValue(count, field, options);
30
+ const usedTerms = new Set();
31
+ const results = [];
32
+ for (let i = 0; i < size; i++) {
33
+ results.push([this._getTerm(field, usedTerms), count]);
34
+ count = this._nextFacetCountValue(count, field, options);
35
+ }
36
+ return results;
31
37
  }
32
- return results;
33
- }
34
38
 
35
- function getTerm(field, usedTerms) {
36
- if (usedTerms.size > 50) {
37
- return fields.term({ field });
38
- }
39
- while (true) {
40
- const term = fields.term({ field });
41
- if (!usedTerms.has(term)) {
42
- usedTerms.add(term);
43
- return term;
39
+ _getTerm(field, usedTerms) {
40
+ const { fields } = this._lorem;
41
+ if (usedTerms.size > 50) {
42
+ return fields.term({ field });
43
+ }
44
+ while (true) {
45
+ const term = fields.term({ field });
46
+ if (!usedTerms.has(term)) {
47
+ usedTerms.add(term);
48
+ return term;
49
+ }
44
50
  }
45
51
  }
46
- }
47
52
 
48
- function highestFacetCountValue(field, options) {
49
- // TODO: capped at _ctrl.total
50
- const [min, max] = HIGHEST_FACET_COUNT_RATIO_RANGE;
51
- return Math.ceil(Math.random() * (max - min) + min);
52
- }
53
+ _highestFacetCountValue(field, options) {
54
+ const [min, max] = HIGHEST_FACET_COUNT_RATIO_RANGE;
55
+ return this._lorem.prng.randomInt(min, max);
56
+ }
57
+
58
+ _nextFacetCountValue(value, field, options) {
59
+ const [min, max] = NEXT_FACET_COUNT_RATIO_RANGE;
60
+ const ratio = min + this._lorem.prng.random() * (max - min);
61
+ return Math.ceil(value * ratio);
62
+ }
53
63
 
54
- function nextFacetCountValue(value, field, options) {
55
- const [min, max] = NEXT_FACET_COUNT_RATIO_RANGE;
56
- const ratio = Math.random() * (max - min) + min;
57
- return Math.ceil(value * ratio);
58
64
  }
@@ -1,22 +1,24 @@
1
- import { fields, utils } from '@miso.ai/lorem';
1
+ export class Images {
2
2
 
3
- export function *images({ rows, ...options } = {}) {
4
- for (let i = 0; i < rows; i++) {
5
- yield image({ ...options, index: i });
3
+ constructor(data) {
4
+ this._lorem = data._lorem;
6
5
  }
7
- }
8
6
 
9
- function image({ fl = [] } = {}) {
10
- const id = utils.id();
7
+ _image({ fl = [] } = {}) {
8
+ const { fields, prng } = this._lorem;
9
+
10
+ const id = prng.shortId();
11
+
12
+ return {
13
+ product_id: id,
14
+ image_src: fields.image({ size: [1200, 400] }),
15
+ image_alt: fields.title({ size: [2, 4] }),
16
+ title: fields.title({ size: [4, 10] }),
17
+ url: `/products/${id}`,
18
+ created_at: fields.date(),
19
+ updated_at: fields.date(),
20
+ published_at: fields.date(),
21
+ };
22
+ }
11
23
 
12
- return {
13
- product_id: id,
14
- image_src: fields.image({ size: [1200, 400] }),
15
- image_alt: fields.title({ size: [2, 4] }),
16
- title: fields.title({ size: [4, 10] }),
17
- url: `/products/${id}`,
18
- created_at: fields.date(),
19
- updated_at: fields.date(),
20
- published_at: fields.date(),
21
- };
22
24
  }
package/lib/data/index.js CHANGED
@@ -1,8 +1,4 @@
1
- export * from '@miso.ai/lorem';
2
- export * from './products.js';
3
- export * from './articles.js';
4
- export * from './questions.js';
5
- export * from './answers.js';
6
- export * from './search.js';
7
- export * from './completions.js';
8
- export * from './images.js';
1
+ export * from './data.js';
2
+
3
+ // Re-export lorem
4
+ export { lorem } from '@miso.ai/lorem';
@@ -1,29 +1,30 @@
1
- import { fields, utils } from '@miso.ai/lorem';
1
+ export class Products {
2
2
 
3
- export function *products({ rows, ...options } = {}) {
4
- for (let i = 0; i < rows; i++) {
5
- yield product({ ...options, index: i });
3
+ constructor(data) {
4
+ this._lorem = data._lorem;
6
5
  }
7
- }
8
6
 
9
- function product({} = {}) {
10
- const id = utils.id();
11
- const prices = utils.repeat(fields.price, [1, 2]);
12
- prices.sort();
7
+ _product({} = {}) {
8
+ const { fields, prng, utils } = this._lorem;
9
+
10
+ const id = prng.shortId();
11
+ const prices = utils.repeat(() => fields.price(), [1, 2]);
12
+ prices.sort((a, b) => a - b);
13
+
14
+ return {
15
+ product_id: id,
16
+ authors: fields.authors(),
17
+ categories: [],
18
+ tags: fields.tags(),
19
+ title: fields.title(),
20
+ description: fields.description(),
21
+ cover_image: fields.image(),
22
+ url: `https://dummy.miso.ai/products/${id}`,
23
+ sale_price: prices[0],
24
+ original_price: prices[prices.length - 1],
25
+ rating: fields.rating(),
26
+ availability: fields.availability(),
27
+ };
28
+ }
13
29
 
14
- return {
15
- product_id: id,
16
- authors: fields.authors(),
17
- categories: [],
18
- tags: fields.tags(),
19
- title: fields.title(),
20
- description: fields.description(),
21
- //html,
22
- cover_image: fields.image(),
23
- url: `https://dummy.miso.ai/products/${id}`,
24
- sale_price: prices[0],
25
- original_price: prices[prices.length - 1],
26
- rating: fields.rating(),
27
- availability: fields.availability(),
28
- };
29
30
  }
@@ -1,11 +1,11 @@
1
- import { fields } from '@miso.ai/lorem';
1
+ export class Questions {
2
2
 
3
- export function *questions({ rows = 5, ...options } = {}) {
4
- for (let i = 0; i < rows; i++) {
5
- yield question({ ...options, index: i });
3
+ constructor(data) {
4
+ this._lorem = data._lorem;
5
+ }
6
+
7
+ _question({} = {}) {
8
+ return this._lorem.fields.description({ size: [4, 8], punctuation: '?' });
6
9
  }
7
- }
8
10
 
9
- function question({} = {}) {
10
- return fields.description({ size: [4, 8], punctuation: '?' });
11
11
  }
@@ -1,24 +1,31 @@
1
- import { utils } from '@miso.ai/lorem';
2
- import { articles } from './articles.js';
3
- import { facets as generateFacetFields } from './facets.js';
1
+ import { excludeHtml } from '../utils.js';
4
2
 
5
- export function searchResults({
6
- _ctrl = {},
7
- q,
8
- fl = ['cover_image', 'url'],
9
- rows = 10,
10
- facets,
11
- _meta: {
12
- page = 0,
13
- } = {},
14
- }) {
15
- const total = _ctrl.total !== undefined ? _ctrl.total : utils.randomInt(1000, 10000);
16
- const products = [...articles({ rows: Math.min(total - page * rows, rows), fl })].map(utils.excludeHtml);
17
- const facet_counts = facets ? { facet_fields: generateFacetFields({ facets, _ctrl }) } : undefined;
3
+ export class Search {
4
+
5
+ constructor(data) {
6
+ this._data = data;
7
+ }
8
+
9
+ _searchResults({
10
+ _ctrl = {},
11
+ q,
12
+ fl = ['cover_image', 'url'],
13
+ rows = 10,
14
+ facets,
15
+ _meta: {
16
+ page = 0,
17
+ } = {},
18
+ }) {
19
+ const { prng } = this._data._lorem;
20
+ const total = _ctrl.total !== undefined ? _ctrl.total : prng.randomInt(1000, 10000);
21
+ const products = this._data.articles({ rows: Math.min(total - page * rows, rows), fl }).map(excludeHtml);
22
+ const facet_counts = facets ? { facet_fields: this._data.facets({ facets, _ctrl }) } : undefined;
23
+
24
+ return {
25
+ products,
26
+ total,
27
+ facet_counts,
28
+ };
29
+ }
18
30
 
19
- return {
20
- products,
21
- total,
22
- facet_counts,
23
- };
24
31
  }
package/lib/index.js CHANGED
@@ -1,4 +1,2 @@
1
- export { default as Api } from './api/index.js';
2
- import * as data from './data/index.js';
3
-
4
- export { data };
1
+ export * from './api/index.js';
2
+ export * from './data/index.js';
package/lib/utils.js CHANGED
@@ -10,3 +10,16 @@ export function trimObj(obj) {
10
10
  }
11
11
  return trimmed;
12
12
  }
13
+
14
+ export function formatDatetime(timestamp) {
15
+ const str = new Date(timestamp).toISOString();
16
+ return str.endsWith('Z') ? str.slice(0, -1) : str;
17
+ }
18
+
19
+ export function sample(size, sampling) {
20
+ return sampling !== undefined ? Math.ceil(size * sampling) : size;
21
+ }
22
+
23
+ export function excludeHtml({ html, ...rest }) {
24
+ return rest;
25
+ }
package/package.json CHANGED
@@ -6,7 +6,9 @@
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
- "scripts": {},
9
+ "scripts": {
10
+ "test": "uvu test"
11
+ },
10
12
  "repository": {
11
13
  "type": "git",
12
14
  "url": "git+https://github.com/MisoAI/doggoganger.git"
@@ -20,7 +22,7 @@
20
22
  "url": "https://github.com/MisoAI/doggoganger/issues"
21
23
  },
22
24
  "dependencies": {
23
- "@miso.ai/lorem": "0.0.1"
25
+ "@miso.ai/lorem": "0.10.0-beta.0"
24
26
  },
25
- "version": "0.0.1"
27
+ "version": "0.10.0-beta.0"
26
28
  }
@@ -0,0 +1,51 @@
1
+ import { test } from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+ import { misoData } from '../lib/index.js';
4
+
5
+ const SEED = 42;
6
+
7
+ function generate(seed) {
8
+ const data = misoData({ seed });
9
+
10
+ return {
11
+ // Products
12
+ products: data.products({ rows: 3 }),
13
+
14
+ // Articles
15
+ articles: data.articles({ rows: 3, fl: ['cover_image', 'url'] }),
16
+
17
+ // Questions
18
+ questions: data.questions({ rows: 3 }),
19
+
20
+ // Images
21
+ images: data.images({ rows: 3 }),
22
+
23
+ // Completions
24
+ completions: data.completions({ q: 'test', completion_fields: ['title'], rows: 3 }),
25
+
26
+ // Facets
27
+ facets: data.facets({ facets: ['category', 'brand'] }),
28
+
29
+ // Search results
30
+ searchResults: data.searchResults({ q: 'test', rows: 5 }),
31
+
32
+ // Answer
33
+ answer: data.answer({ question: 'What is this?' }, { answerFormat: 'markdown' }),
34
+ };
35
+ }
36
+
37
+ test('same seed produces identical output', () => {
38
+ const output1 = generate(SEED);
39
+ const output2 = generate(SEED);
40
+
41
+ assert.equal(output1, output2);
42
+ });
43
+
44
+ test('different seeds produce different output', () => {
45
+ const output1 = generate(SEED);
46
+ const output2 = generate(SEED + 1);
47
+
48
+ assert.not.equal(output1, output2);
49
+ });
50
+
51
+ test.run();
package/package.json.bak DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "name": "@miso.ai/doggoganger-api",
3
- "description": "A dummy Miso API module",
4
- "type": "module",
5
- "main": "lib/index.js",
6
- "publishConfig": {
7
- "access": "public"
8
- },
9
- "scripts": {
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/MisoAI/doggoganger.git"
14
- },
15
- "homepage": "https://github.com/MisoAI/doggoganger/",
16
- "license": "MIT",
17
- "contributors": [
18
- "simonpai <simon.pai@askmiso.com>"
19
- ],
20
- "bugs": {
21
- "url": "https://github.com/MisoAI/doggoganger/issues"
22
- },
23
- "dependencies": {
24
- "@miso.ai/lorem": "file:packages/lorem"
25
- }
26
- }