apimo.js 1.0.1 → 1.0.3

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/README.md +314 -48
  2. package/dist/{src/core → core}/api.d.ts +1 -1
  3. package/dist/{src/core → core}/api.js +1 -1
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.js +10 -0
  6. package/dist/{src/schemas → schemas}/agency.d.ts +8 -8
  7. package/dist/{src/schemas → schemas}/property.d.ts +151 -151
  8. package/package.json +16 -2
  9. package/.github/workflows/ci.yml +0 -37
  10. package/.github/workflows/publish.yml +0 -69
  11. package/.idea/apimo.js.iml +0 -13
  12. package/.idea/copilotDiffState.xml +0 -43
  13. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  14. package/.idea/jsLinters/eslint.xml +0 -6
  15. package/.idea/modules.xml +0 -8
  16. package/.idea/prettier.xml +0 -6
  17. package/.idea/vcs.xml +0 -6
  18. package/dist/src/core/api.test.d.ts +0 -1
  19. package/dist/src/core/api.test.js +0 -246
  20. package/dist/src/services/storage/dummy.cache.test.d.ts +0 -1
  21. package/dist/src/services/storage/dummy.cache.test.js +0 -96
  22. package/dist/src/services/storage/filesystem.cache.test.d.ts +0 -1
  23. package/dist/src/services/storage/filesystem.cache.test.js +0 -197
  24. package/dist/src/services/storage/memory.cache.test.d.ts +0 -1
  25. package/dist/src/services/storage/memory.cache.test.js +0 -80
  26. package/dist/src/utils/url.test.d.ts +0 -1
  27. package/dist/src/utils/url.test.js +0 -18
  28. package/dist/vitest.config.d.ts +0 -2
  29. package/dist/vitest.config.js +0 -6
  30. package/eslint.config.mjs +0 -3
  31. package/src/consts/catalogs.ts +0 -55
  32. package/src/consts/languages.ts +0 -22
  33. package/src/core/api.test.ts +0 -308
  34. package/src/core/api.ts +0 -230
  35. package/src/core/converters.ts +0 -7
  36. package/src/schemas/agency.ts +0 -66
  37. package/src/schemas/common.ts +0 -67
  38. package/src/schemas/internal.ts +0 -13
  39. package/src/schemas/property.ts +0 -257
  40. package/src/services/storage/dummy.cache.test.ts +0 -110
  41. package/src/services/storage/dummy.cache.ts +0 -21
  42. package/src/services/storage/filesystem.cache.test.ts +0 -243
  43. package/src/services/storage/filesystem.cache.ts +0 -94
  44. package/src/services/storage/memory.cache.test.ts +0 -94
  45. package/src/services/storage/memory.cache.ts +0 -69
  46. package/src/services/storage/types.ts +0 -20
  47. package/src/types/index.ts +0 -5
  48. package/src/utils/url.test.ts +0 -21
  49. package/src/utils/url.ts +0 -27
  50. package/tsconfig.json +0 -13
  51. package/vitest.config.ts +0 -7
  52. /package/dist/{src/consts → consts}/catalogs.d.ts +0 -0
  53. /package/dist/{src/consts → consts}/catalogs.js +0 -0
  54. /package/dist/{src/consts → consts}/languages.d.ts +0 -0
  55. /package/dist/{src/consts → consts}/languages.js +0 -0
  56. /package/dist/{src/core → core}/converters.d.ts +0 -0
  57. /package/dist/{src/core → core}/converters.js +0 -0
  58. /package/dist/{src/schemas → schemas}/agency.js +0 -0
  59. /package/dist/{src/schemas → schemas}/common.d.ts +0 -0
  60. /package/dist/{src/schemas → schemas}/common.js +0 -0
  61. /package/dist/{src/schemas → schemas}/internal.d.ts +0 -0
  62. /package/dist/{src/schemas → schemas}/internal.js +0 -0
  63. /package/dist/{src/schemas → schemas}/property.js +0 -0
  64. /package/dist/{src/services → services}/storage/dummy.cache.d.ts +0 -0
  65. /package/dist/{src/services → services}/storage/dummy.cache.js +0 -0
  66. /package/dist/{src/services → services}/storage/filesystem.cache.d.ts +0 -0
  67. /package/dist/{src/services → services}/storage/filesystem.cache.js +0 -0
  68. /package/dist/{src/services → services}/storage/memory.cache.d.ts +0 -0
  69. /package/dist/{src/services → services}/storage/memory.cache.js +0 -0
  70. /package/dist/{src/services → services}/storage/types.d.ts +0 -0
  71. /package/dist/{src/services → services}/storage/types.js +0 -0
  72. /package/dist/{src/types → types}/index.d.ts +0 -0
  73. /package/dist/{src/types → types}/index.js +0 -0
  74. /package/dist/{src/utils → utils}/url.d.ts +0 -0
  75. /package/dist/{src/utils → utils}/url.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apimo.js",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A wrapper for the Apimo API with catalog caching for building custom Real Estate website using their technologies.",
5
5
  "author": "Vitaly Lysen <vitaly@lysen.dev> (https://lysen.dev)",
6
6
  "license": "MIT",
@@ -17,7 +17,21 @@
17
17
  "apimo",
18
18
  "real-estate"
19
19
  ],
20
- "main": "/dist/index.js",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.js"
25
+ }
26
+ },
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "files": [
31
+ "LICENSE",
32
+ "README.md",
33
+ "dist/**/*"
34
+ ],
21
35
  "scripts": {
22
36
  "dev": "",
23
37
  "build": "tsc",
@@ -1,37 +0,0 @@
1
- name: Continuous Integration
2
-
3
- on:
4
- push:
5
- branches: [main, develop]
6
- pull_request:
7
- branches: [main, develop]
8
-
9
- jobs:
10
- test-and-lint:
11
- runs-on: ubuntu-latest
12
-
13
- strategy:
14
- matrix:
15
- node-version: [20, 22, 24]
16
-
17
- steps:
18
- - name: Checkout code
19
- uses: actions/checkout@v4
20
-
21
- - name: Setup Node.js ${{ matrix.node-version }}
22
- uses: actions/setup-node@v4
23
- with:
24
- node-version: ${{ matrix.node-version }}
25
- cache: yarn
26
-
27
- - name: Install dependencies
28
- run: yarn install --frozen-lockfile
29
-
30
- - name: Run linting
31
- run: yarn lint
32
-
33
- - name: Run tests
34
- run: yarn test
35
-
36
- - name: Run test coverage
37
- run: yarn test-coverage
@@ -1,69 +0,0 @@
1
- name: Build, Test & Publish to NPM
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*'
7
- workflow_dispatch:
8
-
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - name: Checkout code
15
- uses: actions/checkout@v4
16
-
17
- - name: Setup Node.js
18
- uses: actions/setup-node@v4
19
- with:
20
- node-version: '22'
21
- cache: yarn
22
-
23
- - name: Install dependencies
24
- run: yarn install --frozen-lockfile
25
-
26
- - name: Run linting
27
- run: yarn lint
28
-
29
- - name: Run tests
30
- run: yarn test
31
-
32
- - name: Run test coverage
33
- run: yarn test-coverage
34
-
35
- build-and-publish:
36
- needs: test
37
- runs-on: ubuntu-latest
38
-
39
- environment: Deploy
40
-
41
- steps:
42
- - name: Checkout code
43
- uses: actions/checkout@v4
44
-
45
- - name: Setup Node.js
46
- uses: actions/setup-node@v4
47
- with:
48
- node-version: '22'
49
- cache: yarn
50
- registry-url: 'https://registry.npmjs.org'
51
-
52
- - name: Install dependencies
53
- run: yarn install --frozen-lockfile
54
-
55
- - name: Build package
56
- run: yarn build
57
-
58
- - name: Check if dist directory exists
59
- run: |
60
- if [ ! -d "dist" ]; then
61
- echo "Build failed: dist directory not found"
62
- exit 1
63
- fi
64
- echo "Build successful: dist directory created"
65
-
66
- - name: Publish to NPM
67
- run: npm publish
68
- env:
69
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,13 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$">
5
- <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
- <excludeFolder url="file://$MODULE_DIR$/temp" />
7
- <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
- <excludeFolder url="file://$MODULE_DIR$/dist" />
9
- </content>
10
- <orderEntry type="inheritedJdk" />
11
- <orderEntry type="sourceFolder" forTests="false" />
12
- </component>
13
- </module>
@@ -1,43 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="CopilotDiffPersistence">
4
- <option name="pendingDiffs">
5
- <map>
6
- <entry key="$PROJECT_DIR$/.github/workflows/ci.yml">
7
- <value>
8
- <PendingDiffInfo>
9
- <option name="filePath" value="$PROJECT_DIR$/.github/workflows/ci.yml" />
10
- <option name="updatedContent" value="name: Continuous Integration&#10;&#10;on:&#10; push:&#10; branches: [ main, develop ]&#10; pull_request:&#10; branches: [ main, develop ]&#10;&#10;jobs:&#10; test-and-lint:&#10; runs-on: ubuntu-latest&#10;&#10; strategy:&#10; matrix:&#10; node-version: [18, 20, 22]&#10;&#10; steps:&#10; - name: Checkout code&#10; uses: actions/checkout@v4&#10;&#10; - name: Setup Node.js ${{ matrix.node-version }}&#10; uses: actions/setup-node@v4&#10; with:&#10; node-version: ${{ matrix.node-version }}&#10; cache: 'yarn'&#10;&#10; - name: Install dependencies&#10; run: yarn install --frozen-lockfile&#10;&#10; - name: Run linting&#10; run: yarn lint&#10;&#10; - name: Run tests&#10; run: yarn test&#10;&#10; - name: Run test coverage&#10; run: yarn test-coverage&#10;&#10; - name: Upload coverage reports to Codecov&#10; uses: codecov/codecov-action@v4&#10; if: matrix.node-version == 20&#10; with:&#10; file: ./coverage/lcov.info&#10; fail_ci_if_error: false&#10; env:&#10; CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}" />
11
- </PendingDiffInfo>
12
- </value>
13
- </entry>
14
- <entry key="$PROJECT_DIR$/.github/workflows/publish.yml">
15
- <value>
16
- <PendingDiffInfo>
17
- <option name="filePath" value="$PROJECT_DIR$/.github/workflows/publish.yml" />
18
- <option name="updatedContent" value="name: Build, Test &amp; Publish to NPM&#10;&#10;on:&#10; push:&#10; tags:&#10; - 'v*' # Triggers on version tags like v1.0.0, v1.2.3, etc.&#10; workflow_dispatch: # Allows manual triggering&#10;&#10;jobs:&#10; test:&#10; runs-on: ubuntu-latest&#10; &#10; steps:&#10; - name: Checkout code&#10; uses: actions/checkout@v4&#10; &#10; - name: Setup Node.js&#10; uses: actions/setup-node@v4&#10; with:&#10; node-version: '18'&#10; cache: 'yarn'&#10; &#10; - name: Install dependencies&#10; run: yarn install --frozen-lockfile&#10; &#10; - name: Run linting&#10; run: yarn lint&#10; &#10; - name: Run tests&#10; run: yarn test&#10; &#10; - name: Run test coverage&#10; run: yarn test-coverage&#10;&#10; build-and-publish:&#10; needs: test&#10; runs-on: ubuntu-latest&#10; &#10; steps:&#10; - name: Checkout code&#10; uses: actions/checkout@v4&#10; &#10; - name: Setup Node.js&#10; uses: actions/setup-node@v4&#10; with:&#10; node-version: '18'&#10; cache: 'yarn'&#10; registry-url: 'https://registry.npmjs.org'&#10; &#10; - name: Install dependencies&#10; run: yarn install --frozen-lockfile&#10; &#10; - name: Build package&#10; run: yarn build&#10; &#10; - name: Check if dist directory exists&#10; run: |&#10; if [ ! -d &quot;dist&quot; ]; then&#10; echo &quot;Build failed: dist directory not found&quot;&#10; exit 1&#10; fi&#10; echo &quot;Build successful: dist directory created&quot;&#10; &#10; - name: Publish to NPM&#10; run: npm publish&#10; env:&#10; NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}" />
19
- </PendingDiffInfo>
20
- </value>
21
- </entry>
22
- <entry key="$PROJECT_DIR$/package.json">
23
- <value>
24
- <PendingDiffInfo>
25
- <option name="filePath" value="$PROJECT_DIR$/package.json" />
26
- <option name="originalContent" value="{&#10; &quot;name&quot;: &quot;apimo.js&quot;,&#10; &quot;version&quot;: &quot;1.0.1&quot;,&#10; &quot;description&quot;: &quot;A wrapper for the Apimo API with catalog caching for building custom Real Estate website using their technologies.&quot;,&#10; &quot;main&quot;: &quot;/dist/index.js&quot;,&#10; &quot;repository&quot;: {&#10; &quot;type&quot;: &quot;git&quot;,&#10; &quot;url&quot;: &quot;https://github.com/Neikow/apimo.js.git&quot;&#10; },&#10; &quot;homepage&quot;: &quot;https://github.com/Neikow/apimo.js&quot;,&#10; &quot;bugs&quot;: {&#10; &quot;url&quot;: &quot;https://github.com/Neikow/apimo.js/issues&quot;&#10; },&#10; &quot;keywords&quot;: [&#10; &quot;api&quot;,&#10; &quot;apimo&quot;,&#10; &quot;real-estate&quot;&#10; ],&#10; &quot;scripts&quot;: {&#10; &quot;dev&quot;: &quot;&quot;,&#10; &quot;build&quot;: &quot;tsc&quot;,&#10; &quot;run&quot;: &quot;node dist/index.js&quot;,&#10; &quot;test&quot;: &quot;vitest run&quot;,&#10; &quot;test-coverage&quot;: &quot;vitest run --coverage&quot;&#10; },&#10; &quot;author&quot;: &quot;Vitaly Lysen &lt;vitaly@lysen.dev&gt; (https://lysen.dev)&quot;,&#10; &quot;license&quot;: &quot;MIT&quot;,&#10; &quot;dependencies&quot;: {&#10; &quot;bottleneck&quot;: &quot;^2.19.5&quot;,&#10; &quot;merge-anything&quot;: &quot;^6.0.6&quot;,&#10; &quot;zod&quot;: &quot;^3.21.4&quot;&#10; },&#10; &quot;devDependencies&quot;: {&#10; &quot;@antfu/eslint-config&quot;: &quot;^5.0.0&quot;,&#10; &quot;@types/node&quot;: &quot;^24.1.0&quot;,&#10; &quot;@anatine/zod-mock&quot;: &quot;^3.14.0&quot;,&#10; &quot;@faker-js/faker&quot;: &quot;^9.9.0&quot;,&#10; &quot;@vitest/coverage-v8&quot;: &quot;^3.2.4&quot;,&#10; &quot;dotenv&quot;: &quot;^17.2.1&quot;,&#10; &quot;eslint&quot;: &quot;^9.32.0&quot;,&#10; &quot;typescript&quot;: &quot;^5.9.2&quot;,&#10; &quot;vitest&quot;: &quot;^3.2.4&quot;&#10; }&#10;}&#10;" />
27
- <option name="updatedContent" value="{&#10; &quot;name&quot;: &quot;apimo.js&quot;,&#10; &quot;version&quot;: &quot;1.0.1&quot;,&#10; &quot;description&quot;: &quot;A wrapper for the Apimo API with catalog caching for building custom Real Estate website using their technologies.&quot;,&#10; &quot;main&quot;: &quot;/dist/index.js&quot;,&#10; &quot;repository&quot;: {&#10; &quot;type&quot;: &quot;git&quot;,&#10; &quot;url&quot;: &quot;https://github.com/Neikow/apimo.js.git&quot;&#10; },&#10; &quot;homepage&quot;: &quot;https://github.com/Neikow/apimo.js&quot;,&#10; &quot;bugs&quot;: {&#10; &quot;url&quot;: &quot;https://github.com/Neikow/apimo.js/issues&quot;&#10; },&#10; &quot;keywords&quot;: [&#10; &quot;api&quot;,&#10; &quot;apimo&quot;,&#10; &quot;real-estate&quot;&#10; ],&#10; &quot;scripts&quot;: {&#10; &quot;dev&quot;: &quot;&quot;,&#10; &quot;build&quot;: &quot;tsc&quot;,&#10; &quot;run&quot;: &quot;node dist/index.js&quot;,&#10; &quot;test&quot;: &quot;vitest run&quot;,&#10; &quot;test-coverage&quot;: &quot;vitest run --coverage&quot;,&#10; &quot;lint&quot;: &quot;eslint .&quot;&#10; },&#10; &quot;author&quot;: &quot;Vitaly Lysen &lt;vitaly@lysen.dev&gt; (https://lysen.dev)&quot;,&#10; &quot;license&quot;: &quot;MIT&quot;,&#10; &quot;dependencies&quot;: {&#10; &quot;bottleneck&quot;: &quot;^2.19.5&quot;,&#10; &quot;merge-anything&quot;: &quot;^6.0.6&quot;,&#10; &quot;zod&quot;: &quot;^3.21.4&quot;&#10; },&#10; &quot;devDependencies&quot;: {&#10; &quot;@antfu/eslint-config&quot;: &quot;^5.0.0&quot;,&#10; &quot;@types/node&quot;: &quot;^24.1.0&quot;,&#10; &quot;@anatine/zod-mock&quot;: &quot;^3.14.0&quot;,&#10; &quot;@faker-js/faker&quot;: &quot;^9.9.0&quot;,&#10; &quot;@vitest/coverage-v8&quot;: &quot;^3.2.4&quot;,&#10; &quot;dotenv&quot;: &quot;^17.2.1&quot;,&#10; &quot;eslint&quot;: &quot;^9.32.0&quot;,&#10; &quot;typescript&quot;: &quot;^5.9.2&quot;,&#10; &quot;vitest&quot;: &quot;^3.2.4&quot;&#10; }&#10;}" />
28
- </PendingDiffInfo>
29
- </value>
30
- </entry>
31
- <entry key="$PROJECT_DIR$/src/services/storage/dummy.cache.test.ts">
32
- <value>
33
- <PendingDiffInfo>
34
- <option name="filePath" value="$PROJECT_DIR$/src/services/storage/dummy.cache.test.ts" />
35
- <option name="originalContent" value="import type { CatalogName } from '../../consts/catalogs'&#10;import type { ApiCulture } from '../../consts/languages'&#10;import { afterEach, beforeEach, describe, expect, it } from 'vitest'&#10;import { DummyCache } from './dummy.cache'&#10;import { CacheExpiredError } from './types'&#10;&#10;describe('cache - Dummy', () =&gt; {&#10; let cache: DummyCache&#10;&#10; beforeEach(() =&gt; {&#10; cache = new DummyCache()&#10; })&#10;&#10; afterEach(() =&gt; {&#10; // No cleanup needed for dummy cache&#10; })&#10;&#10; describe('constructor', () =&gt; {&#10; it('should create an instance without any configuration', () =&gt; {&#10; const dummyCache = new DummyCache()&#10; expect(dummyCache).toBeInstanceOf(DummyCache)&#10; })&#10; })&#10;&#10; describe('setEntries', () =&gt; {&#10; const culture: ApiCulture = 'en'&#10; const entries = [&#10; { id: 1, name: 'Item 1', name_plurial: 'Items 1' },&#10; { id: 2, name: 'Item 2', name_plurial: 'Items 2' },&#10; ]&#10;&#10; it('should not throw when setting entries', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined()&#10; })&#10;&#10; it('should handle empty entries array', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.setEntries(catalogName, culture, [])).resolves.toBeUndefined()&#10; })&#10;&#10; it('should handle different catalog and culture combinations', async () =&gt; {&#10; await expect(cache.setEntries('book_step', 'en', entries)).resolves.toBeUndefined()&#10; await expect(cache.setEntries('property_land', 'fr', entries)).resolves.toBeUndefined()&#10; await expect(cache.setEntries('property_type', 'de', entries)).resolves.toBeUndefined()&#10; })&#10; })&#10;&#10; describe('getEntry', () =&gt; {&#10; const culture: ApiCulture = 'en'&#10;&#10; it('should always throw CacheExpiredError regardless of parameters', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError for any ID', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.getEntry(catalogName, culture, 999)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry(catalogName, culture, 0)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry(catalogName, culture, -1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError for different catalogs and cultures', async () =&gt; {&#10; await expect(cache.getEntry('book_step', 'en', 1)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry('property_land', 'fr', 1)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry('property_type', 'de', 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError even after setting entries', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; const entries = [{ id: 1, name: 'Item 1', name_plurial: 'Items 1' }]&#10;&#10; await cache.setEntries(catalogName, culture, entries)&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10; })&#10;})&#10;" />
36
- <option name="updatedContent" value="import type { CatalogName } from '../../consts/catalogs'&#10;import type { ApiCulture } from '../../consts/languages'&#10;import { afterEach, beforeEach, describe, expect, it } from 'vitest'&#10;import { DummyCache } from './dummy.cache'&#10;import { CacheExpiredError } from './types'&#10;&#10;describe('cache - Dummy', () =&gt; {&#10; let cache: DummyCache&#10;&#10; beforeEach(() =&gt; {&#10; cache = new DummyCache()&#10; })&#10;&#10; afterEach(() =&gt; {&#10; // No cleanup needed for dummy cache&#10; })&#10;&#10; describe('constructor', () =&gt; {&#10; it('should create an instance without any configuration', () =&gt; {&#10; const dummyCache = new DummyCache()&#10; expect(dummyCache).toBeInstanceOf(DummyCache)&#10; })&#10; })&#10;&#10; describe('setEntries', () =&gt; {&#10; const culture: ApiCulture = 'en'&#10; const entries = [&#10; { id: 1, name: 'Item 1', name_plurial: 'Items 1' },&#10; { id: 2, name: 'Item 2', name_plurial: 'Items 2' },&#10; ]&#10;&#10; it('should not throw when setting entries', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined()&#10; })&#10;&#10; it('should handle empty entries array', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.setEntries(catalogName, culture, [])).resolves.toBeUndefined()&#10; })&#10;&#10; it('should handle different catalog and culture combinations', async () =&gt; {&#10; await expect(cache.setEntries('book_step', 'en', entries)).resolves.toBeUndefined()&#10; await expect(cache.setEntries('property_land', 'fr', entries)).resolves.toBeUndefined()&#10; await expect(cache.setEntries('property_type', 'de', entries)).resolves.toBeUndefined()&#10; })&#10; })&#10;&#10; describe('getEntry', () =&gt; {&#10; const culture: ApiCulture = 'en'&#10;&#10; it('should always throw CacheExpiredError regardless of parameters', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError for any ID', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; await expect(cache.getEntry(catalogName, culture, 999)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry(catalogName, culture, 0)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry(catalogName, culture, -1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError for different catalogs and cultures', async () =&gt; {&#10; await expect(cache.getEntry('book_step', 'en', 1)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry('property_land', 'fr', 1)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry('property_type', 'de', 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should throw CacheExpiredError even after setting entries', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; const entries = [{ id: 1, name: 'Item 1', name_plurial: 'Items 1' }]&#10;&#10; await cache.setEntries(catalogName, culture, entries)&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10; })&#10;&#10; describe('behavior consistency', () =&gt; {&#10; it('should behave consistently across multiple calls', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; const culture: ApiCulture = 'en'&#10; const entries = [{ id: 1, name: 'Item 1', name_plurial: 'Items 1' }]&#10;&#10; // Multiple setEntries calls should not throw&#10; await expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined()&#10; await expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined()&#10;&#10; // Multiple getEntry calls should always throw&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10; })&#10;&#10; it('should maintain dummy behavior regardless of cache state', async () =&gt; {&#10; const catalogName: CatalogName = 'book_step'&#10; const culture: ApiCulture = 'en'&#10;&#10; // Should throw before any operations&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10;&#10; // Should still throw after setting entries&#10; await cache.setEntries(catalogName, culture, [{ id: 1, name: 'Test', name_plurial: 'Tests' }])&#10; await expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError)&#10;&#10; // Should still throw after multiple operations&#10; await cache.setEntries(catalogName, culture, [])&#10; await expect(cache.getEntry(catalogName, culture, 999)).rejects.toThrow(CacheExpiredError)&#10; })&#10; })&#10;})" />
37
- </PendingDiffInfo>
38
- </value>
39
- </entry>
40
- </map>
41
- </option>
42
- </component>
43
- </project>
@@ -1,6 +0,0 @@
1
- <component name="InspectionProjectProfileManager">
2
- <profile version="1.0">
3
- <option name="myName" value="Project Default" />
4
- <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
5
- </profile>
6
- </component>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="EslintConfiguration">
4
- <option name="fix-on-save" value="true" />
5
- </component>
6
- </project>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/apimo.js.iml" filepath="$PROJECT_DIR$/.idea/apimo.js.iml" />
6
- </modules>
7
- </component>
8
- </project>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="PrettierConfiguration">
4
- <option name="myConfigurationMode" value="AUTOMATIC" />
5
- </component>
6
- </project>
package/.idea/vcs.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
- </component>
6
- </project>
@@ -1 +0,0 @@
1
- export {};
@@ -1,246 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { afterEach, beforeEach, it as defaultIt, describe, expect, vi } from 'vitest';
11
- import { z } from 'zod';
12
- import { DummyCache } from '../services/storage/dummy.cache';
13
- import { MemoryCache } from '../services/storage/memory.cache';
14
- import { Api, DEFAULT_BASE_URL } from './api';
15
- // Mock fetch globally
16
- const mockFetch = vi.fn();
17
- const PROVIDER = '0';
18
- const TOKEN = 'TOKEN';
19
- const BasicAuthHeaders = {
20
- Authorization: `Basic ${btoa(`${PROVIDER}:${TOKEN}`)}`,
21
- };
22
- const it = defaultIt.extend({
23
- // eslint-disable-next-line no-empty-pattern
24
- api: (_a, use_1) => __awaiter(void 0, [_a, use_1], void 0, function* ({}, use) {
25
- let api = new Api('0', 'TOKEN', {
26
- catalogs: {
27
- transform: {
28
- active: false,
29
- },
30
- },
31
- });
32
- yield use(api);
33
- api = null;
34
- }),
35
- // eslint-disable-next-line no-empty-pattern
36
- mockResponse: (_a, use_1) => __awaiter(void 0, [_a, use_1], void 0, function* ({}, use) {
37
- const mockResponse = (config) => {
38
- var _a, _b;
39
- mockFetch.mockResolvedValue({
40
- ok: (_a = config === null || config === void 0 ? void 0 : config.ok) !== null && _a !== void 0 ? _a : true,
41
- status: (_b = config === null || config === void 0 ? void 0 : config.status) !== null && _b !== void 0 ? _b : 200,
42
- json: (config === null || config === void 0 ? void 0 : config.json) ? vi.fn().mockResolvedValue(config.json()) : vi.fn().mockResolvedValue({}),
43
- text: vi.fn(),
44
- headers: new Headers(),
45
- statusText: 'OK',
46
- url: '',
47
- redirected: false,
48
- type: 'basic',
49
- body: null,
50
- bodyUsed: false,
51
- clone: vi.fn(),
52
- arrayBuffer: vi.fn(),
53
- blob: vi.fn(),
54
- formData: vi.fn(),
55
- });
56
- };
57
- yield use(mockResponse);
58
- }),
59
- });
60
- describe('api', () => {
61
- let mockResponse;
62
- beforeEach(() => {
63
- // Mock global fetch
64
- vi.stubGlobal('fetch', mockFetch);
65
- // Create a mock response object
66
- mockResponse = {
67
- ok: true,
68
- status: 200,
69
- json: vi.fn(),
70
- text: vi.fn(),
71
- headers: new Headers(),
72
- statusText: 'OK',
73
- url: '',
74
- redirected: false,
75
- type: 'basic',
76
- body: null,
77
- bodyUsed: false,
78
- clone: vi.fn(),
79
- arrayBuffer: vi.fn(),
80
- blob: vi.fn(),
81
- formData: vi.fn(),
82
- };
83
- mockFetch.mockResolvedValue(mockResponse);
84
- });
85
- afterEach(() => {
86
- vi.unstubAllGlobals();
87
- vi.clearAllMocks();
88
- });
89
- describe('constructor', () => {
90
- it('should accept a provider, a token and a base config', ({ api }) => {
91
- expect(api).toBeInstanceOf(Api);
92
- });
93
- it('should use default config when no additional config provided', ({ api }) => {
94
- expect(api.config).toStrictEqual({
95
- baseUrl: DEFAULT_BASE_URL,
96
- culture: 'en',
97
- catalogs: {
98
- cache: {
99
- active: true,
100
- adapter: expect.any(MemoryCache),
101
- },
102
- transform: {
103
- active: false,
104
- },
105
- },
106
- });
107
- });
108
- it('should merge custom config with defaults', () => {
109
- const testApi = new Api('provider', 'token', {
110
- baseUrl: 'https://custom.api.com',
111
- culture: 'fr',
112
- catalogs: {
113
- cache: {
114
- active: false,
115
- adapter: new DummyCache(),
116
- },
117
- },
118
- });
119
- expect(testApi.config).toStrictEqual({
120
- baseUrl: 'https://custom.api.com',
121
- culture: 'fr',
122
- catalogs: {
123
- cache: {
124
- active: false,
125
- adapter: expect.any(DummyCache),
126
- },
127
- transform: {
128
- active: true,
129
- },
130
- },
131
- });
132
- });
133
- it('should use provided cache adapter', () => {
134
- const testApi = new Api('provider', 'token', {
135
- catalogs: {
136
- cache: {
137
- adapter: new DummyCache(),
138
- },
139
- },
140
- });
141
- expect(testApi.cache).toBeInstanceOf(DummyCache);
142
- });
143
- it('should use DummyCache when cache is not active', () => {
144
- const testApi = new Api('provider', 'token', {
145
- catalogs: {
146
- cache: {
147
- active: false,
148
- adapter: new MemoryCache(),
149
- },
150
- },
151
- });
152
- expect(testApi.cache).toBeInstanceOf(DummyCache);
153
- });
154
- });
155
- describe('fetch', () => {
156
- it('should have the right authorization headers when fetching', () => __awaiter(void 0, void 0, void 0, function* () {
157
- const testApi = new Api('provider', 'token');
158
- yield testApi.fetch(DEFAULT_BASE_URL);
159
- expect(mockFetch).toHaveBeenCalledExactlyOnceWith(DEFAULT_BASE_URL, {
160
- headers: {
161
- Authorization: `Basic ${btoa('provider:token')}`,
162
- },
163
- });
164
- }));
165
- it('should merge additional headers with authorization', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api }) {
166
- const customHeaders = { 'Content-Type': 'application/json' };
167
- yield api.fetch(DEFAULT_BASE_URL, { headers: customHeaders });
168
- expect(mockFetch).toHaveBeenCalledWith(DEFAULT_BASE_URL, {
169
- headers: Object.assign(Object.assign({}, BasicAuthHeaders), { 'Content-Type': 'application/json' }),
170
- });
171
- }));
172
- it('should pass through other fetch options', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api }) {
173
- const options = {
174
- method: 'POST',
175
- body: JSON.stringify({ test: 'data' }),
176
- headers: { 'Custom-Header': 'value' },
177
- };
178
- yield api.fetch(DEFAULT_BASE_URL, options);
179
- expect(mockFetch).toHaveBeenCalledWith(DEFAULT_BASE_URL, {
180
- method: 'POST',
181
- body: JSON.stringify({ test: 'data' }),
182
- headers: Object.assign(Object.assign({}, BasicAuthHeaders), { 'Custom-Header': 'value' }),
183
- });
184
- }));
185
- it('should handle rate limiting with Bottleneck', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api }) {
186
- // Make multiple concurrent requests to test rate limiting
187
- const promises = Array.from({ length: 3 }, () => api.fetch(DEFAULT_BASE_URL));
188
- yield Promise.all(promises);
189
- expect(mockFetch).toHaveBeenCalledTimes(3);
190
- }));
191
- });
192
- describe('get', () => {
193
- it('should fetch and parse according to the specified schema', (_a) => __awaiter(void 0, [_a], void 0, function* ({ mockResponse, api }) {
194
- mockResponse({
195
- json: () => ({ success: true }),
196
- });
197
- const spy = vi.spyOn(api, 'fetch');
198
- yield api.get(['path', 'to', 'catalogs'], z.object({ success: z.boolean() }), { culture: 'en' });
199
- expect(spy).toHaveBeenCalledExactlyOnceWith(new URL('https://api.apimo.pro/path/to/catalogs?culture=en'));
200
- }));
201
- });
202
- describe('populateCache', () => {
203
- it('should populate cache without returning entry when no id provided', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api, mockResponse }) {
204
- const catalogName = 'property_type';
205
- const culture = 'en';
206
- const mockEntries = [
207
- { id: 1, name: 'Apartment', name_plurial: 'Apartments' },
208
- { id: 2, name: 'House', name_plurial: 'Houses' },
209
- ];
210
- mockResponse({
211
- json: () => mockEntries,
212
- });
213
- const result = yield api.populateCache(catalogName, culture);
214
- expect(result).toBeUndefined();
215
- expect(mockFetch).toHaveBeenCalledExactlyOnceWith(new URL(`https://api.apimo.pro/catalogs/${catalogName}?culture=${culture}`), {
216
- headers: BasicAuthHeaders,
217
- });
218
- }));
219
- it('should populate cache and return specific entry when id provided', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api, mockResponse }) {
220
- const catalogName = 'property_type';
221
- const culture = 'en';
222
- const mockEntries = [
223
- { id: 1, name: 'Apartment', name_plurial: 'Apartments' },
224
- { id: 2, name: 'House', name_plurial: 'Houses' },
225
- ];
226
- mockResponse({
227
- json: () => mockEntries,
228
- });
229
- const result = yield api.populateCache(catalogName, culture, 1);
230
- expect(result).toEqual({
231
- name: 'Apartment',
232
- namePlural: 'Apartments',
233
- });
234
- }));
235
- it('should return null when requested id not found', (_a) => __awaiter(void 0, [_a], void 0, function* ({ api, mockResponse }) {
236
- const catalogName = 'property_type';
237
- const culture = 'en';
238
- const mockEntries = [
239
- { id: 1, name: 'Apartment', name_plurial: 'Apartments' },
240
- ];
241
- mockResponse({ json: () => mockEntries });
242
- const result = yield api.populateCache(catalogName, culture, 999);
243
- expect(result).toBeNull();
244
- }));
245
- });
246
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,96 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
11
- import { DummyCache } from './dummy.cache';
12
- import { CacheExpiredError } from './types';
13
- describe('cache - Dummy', () => {
14
- let cache;
15
- beforeEach(() => {
16
- cache = new DummyCache();
17
- });
18
- afterEach(() => {
19
- // No cleanup needed for dummy cache
20
- });
21
- describe('constructor', () => {
22
- it('should create an instance without any configuration', () => {
23
- const dummyCache = new DummyCache();
24
- expect(dummyCache).toBeInstanceOf(DummyCache);
25
- });
26
- });
27
- describe('setEntries', () => {
28
- const culture = 'en';
29
- const entries = [
30
- { id: 1, name: 'Item 1', name_plurial: 'Items 1' },
31
- { id: 2, name: 'Item 2', name_plurial: 'Items 2' },
32
- ];
33
- it('should not throw when setting entries', () => __awaiter(void 0, void 0, void 0, function* () {
34
- const catalogName = 'book_step';
35
- yield expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined();
36
- }));
37
- it('should handle empty entries array', () => __awaiter(void 0, void 0, void 0, function* () {
38
- const catalogName = 'book_step';
39
- yield expect(cache.setEntries(catalogName, culture, [])).resolves.toBeUndefined();
40
- }));
41
- it('should handle different catalog and culture combinations', () => __awaiter(void 0, void 0, void 0, function* () {
42
- yield expect(cache.setEntries('book_step', 'en', entries)).resolves.toBeUndefined();
43
- yield expect(cache.setEntries('property_land', 'fr', entries)).resolves.toBeUndefined();
44
- yield expect(cache.setEntries('property_type', 'de', entries)).resolves.toBeUndefined();
45
- }));
46
- });
47
- describe('getEntry', () => {
48
- const culture = 'en';
49
- it('should always throw CacheExpiredError regardless of parameters', () => __awaiter(void 0, void 0, void 0, function* () {
50
- const catalogName = 'book_step';
51
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
52
- }));
53
- it('should throw CacheExpiredError for any ID', () => __awaiter(void 0, void 0, void 0, function* () {
54
- const catalogName = 'book_step';
55
- yield expect(cache.getEntry(catalogName, culture, 999)).rejects.toThrow(CacheExpiredError);
56
- yield expect(cache.getEntry(catalogName, culture, 0)).rejects.toThrow(CacheExpiredError);
57
- yield expect(cache.getEntry(catalogName, culture, -1)).rejects.toThrow(CacheExpiredError);
58
- }));
59
- it('should throw CacheExpiredError for different catalogs and cultures', () => __awaiter(void 0, void 0, void 0, function* () {
60
- yield expect(cache.getEntry('book_step', 'en', 1)).rejects.toThrow(CacheExpiredError);
61
- yield expect(cache.getEntry('property_land', 'fr', 1)).rejects.toThrow(CacheExpiredError);
62
- yield expect(cache.getEntry('property_type', 'de', 1)).rejects.toThrow(CacheExpiredError);
63
- }));
64
- it('should throw CacheExpiredError even after setting entries', () => __awaiter(void 0, void 0, void 0, function* () {
65
- const catalogName = 'book_step';
66
- const entries = [{ id: 1, name: 'Item 1', name_plurial: 'Items 1' }];
67
- yield cache.setEntries(catalogName, culture, entries);
68
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
69
- }));
70
- });
71
- describe('behavior consistency', () => {
72
- it('should behave consistently across multiple calls', () => __awaiter(void 0, void 0, void 0, function* () {
73
- const catalogName = 'book_step';
74
- const culture = 'en';
75
- const entries = [{ id: 1, name: 'Item 1', name_plurial: 'Items 1' }];
76
- // Multiple setEntries calls should not throw
77
- yield expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined();
78
- yield expect(cache.setEntries(catalogName, culture, entries)).resolves.toBeUndefined();
79
- // Multiple getEntry calls should always throw
80
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
81
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
82
- }));
83
- it('should maintain dummy behavior regardless of cache state', () => __awaiter(void 0, void 0, void 0, function* () {
84
- const catalogName = 'book_step';
85
- const culture = 'en';
86
- // Should throw before any operations
87
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
88
- // Should still throw after setting entries
89
- yield cache.setEntries(catalogName, culture, [{ id: 1, name: 'Test', name_plurial: 'Tests' }]);
90
- yield expect(cache.getEntry(catalogName, culture, 1)).rejects.toThrow(CacheExpiredError);
91
- // Should still throw after multiple operations
92
- yield cache.setEntries(catalogName, culture, []);
93
- yield expect(cache.getEntry(catalogName, culture, 999)).rejects.toThrow(CacheExpiredError);
94
- }));
95
- });
96
- });
@@ -1 +0,0 @@
1
- export {};