@basemaps/lambda-tiler 6.32.1 → 6.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/build/__tests__/xyz.util.d.ts +1 -1
  3. package/build/__tests__/xyz.util.d.ts.map +1 -1
  4. package/build/__tests__/xyz.util.js +2 -2
  5. package/build/__tests__/xyz.util.js.map +1 -1
  6. package/build/arcgis/__tests__/arcgis.style.json.test.d.ts +2 -0
  7. package/build/arcgis/__tests__/arcgis.style.json.test.d.ts.map +1 -0
  8. package/build/arcgis/__tests__/arcgis.style.json.test.js +128 -0
  9. package/build/arcgis/__tests__/arcgis.style.json.test.js.map +1 -0
  10. package/build/arcgis/__tests__/vector.tiler.server.test.d.ts +2 -0
  11. package/build/arcgis/__tests__/vector.tiler.server.test.d.ts.map +1 -0
  12. package/build/arcgis/__tests__/vector.tiler.server.test.js +47 -0
  13. package/build/arcgis/__tests__/vector.tiler.server.test.js.map +1 -0
  14. package/build/arcgis/arcgis.info.d.ts +3 -0
  15. package/build/arcgis/arcgis.info.d.ts.map +1 -0
  16. package/build/arcgis/arcgis.info.js +25 -0
  17. package/build/arcgis/arcgis.info.js.map +1 -0
  18. package/build/arcgis/arcgis.style.json.d.ts +9 -0
  19. package/build/arcgis/arcgis.style.json.d.ts.map +1 -0
  20. package/build/arcgis/arcgis.style.json.js +73 -0
  21. package/build/arcgis/arcgis.style.json.js.map +1 -0
  22. package/build/arcgis/vector.tile.server.d.ts +8 -0
  23. package/build/arcgis/vector.tile.server.d.ts.map +1 -0
  24. package/build/arcgis/vector.tile.server.js +71 -0
  25. package/build/arcgis/vector.tile.server.js.map +1 -0
  26. package/build/index.d.ts.map +1 -1
  27. package/build/index.js +22 -8
  28. package/build/index.js.map +1 -1
  29. package/build/routes/__tests__/fonts.test.js +14 -26
  30. package/build/routes/__tests__/fonts.test.js.map +1 -1
  31. package/build/routes/__tests__/sprites.test.js +7 -1
  32. package/build/routes/__tests__/sprites.test.js.map +1 -1
  33. package/build/routes/__tests__/xyz.test.js +1 -2
  34. package/build/routes/__tests__/xyz.test.js.map +1 -1
  35. package/build/routes/fonts.d.ts +0 -2
  36. package/build/routes/fonts.d.ts.map +1 -1
  37. package/build/routes/fonts.js +3 -66
  38. package/build/routes/fonts.js.map +1 -1
  39. package/build/routes/sprites.d.ts.map +1 -1
  40. package/build/routes/sprites.js +3 -29
  41. package/build/routes/sprites.js.map +1 -1
  42. package/build/routes/tile.xyz.vector.js +2 -2
  43. package/build/routes/tile.xyz.vector.js.map +1 -1
  44. package/build/util/assets.provider.d.ts +27 -0
  45. package/build/util/assets.provider.d.ts.map +1 -0
  46. package/build/util/assets.provider.js +56 -0
  47. package/build/util/assets.provider.js.map +1 -0
  48. package/build/util/config.cache.d.ts +16 -0
  49. package/build/util/config.cache.d.ts.map +1 -0
  50. package/build/util/config.cache.js +41 -0
  51. package/build/util/config.cache.js.map +1 -0
  52. package/build/util/response.d.ts +3 -1
  53. package/build/util/response.d.ts.map +1 -1
  54. package/build/util/response.js +2 -0
  55. package/build/util/response.js.map +1 -1
  56. package/build/util/source.cache.d.ts +1 -0
  57. package/build/util/source.cache.d.ts.map +1 -1
  58. package/build/util/source.cache.js +2 -1
  59. package/build/util/source.cache.js.map +1 -1
  60. package/build/util/swapping.lru.d.ts +1 -0
  61. package/build/util/swapping.lru.d.ts.map +1 -1
  62. package/build/util/swapping.lru.js +7 -0
  63. package/build/util/swapping.lru.js.map +1 -1
  64. package/build/util/validate.d.ts +0 -5
  65. package/build/util/validate.d.ts.map +1 -1
  66. package/build/util/validate.js +1 -23
  67. package/build/util/validate.js.map +1 -1
  68. package/dist/index.js +52 -52
  69. package/dist/node_modules/.package-lock.json +4 -4
  70. package/dist/node_modules/node-abi/abi_registry.json +8 -1
  71. package/dist/node_modules/node-abi/package.json +1 -1
  72. package/dist/package-lock.json +8 -8
  73. package/dist/package.json +1 -1
  74. package/package.json +4 -4
  75. package/src/__tests__/xyz.util.ts +7 -2
  76. package/src/arcgis/__tests__/arcgis.style.json.test.ts +153 -0
  77. package/src/arcgis/__tests__/vector.tiler.server.test.ts +61 -0
  78. package/src/arcgis/arcgis.info.ts +26 -0
  79. package/src/arcgis/arcgis.style.json.ts +81 -0
  80. package/src/arcgis/vector.tile.server.ts +78 -0
  81. package/src/index.ts +25 -8
  82. package/src/routes/__tests__/fonts.test.ts +14 -29
  83. package/src/routes/__tests__/sprites.test.ts +7 -2
  84. package/src/routes/__tests__/xyz.test.ts +2 -2
  85. package/src/routes/fonts.ts +4 -64
  86. package/src/routes/sprites.ts +4 -27
  87. package/src/routes/tile.xyz.vector.ts +2 -2
  88. package/src/util/assets.provider.ts +67 -0
  89. package/src/util/config.cache.ts +44 -0
  90. package/src/util/response.ts +4 -1
  91. package/src/util/source.cache.ts +2 -1
  92. package/src/util/swapping.lru.ts +7 -0
  93. package/src/util/validate.ts +1 -27
  94. package/tsconfig.json +1 -0
  95. package/tsconfig.tsbuildinfo +1 -1
@@ -2,8 +2,9 @@ import { Env } from '@basemaps/shared';
2
2
  import { fsa } from '@chunkd/fs';
3
3
  import o from 'ospec';
4
4
  import { handler } from '../../index.js';
5
+ import { assetProvider } from '../../util/assets.provider.js';
5
6
  import { mockRequest } from '../../__tests__/xyz.util.js';
6
- import { fontList, getFonts } from '../fonts.js';
7
+ import { fontList } from '../fonts.js';
7
8
  import { FsMemory } from './memory.fs.js';
8
9
 
9
10
  o.spec('/v1/fonts', () => {
@@ -11,52 +12,30 @@ o.spec('/v1/fonts', () => {
11
12
  o.before(() => {
12
13
  fsa.register('memory://', memory);
13
14
  });
15
+ const assetLocation = process.env[Env.AssetLocation];
14
16
 
15
17
  o.beforeEach(() => {
16
18
  process.env[Env.AssetLocation] = 'memory://';
19
+ assetProvider.set('memory://');
17
20
  });
18
21
 
19
22
  o.afterEach(() => {
20
- delete process.env[Env.AssetLocation];
23
+ assetProvider.set(assetLocation);
21
24
  memory.files.clear();
22
25
  });
23
26
 
24
- o('should list font types', async () => {
25
- await Promise.all([
26
- fsa.write('memory://fonts/Roboto Thin/0-255.pbf', Buffer.from('')),
27
- fsa.write('memory://fonts/Roboto Thin/256-512.pbf', Buffer.from('')),
28
- fsa.write('memory://fonts/Roboto Black/0-255.pbf', Buffer.from('')),
29
- ]);
30
-
31
- const fonts = await getFonts('memory://fonts/');
32
- o(fonts).deepEquals(['Roboto Black', 'Roboto Thin']);
33
- });
34
-
35
- o('should return empty list if no fonts found', async () => {
36
- const res = await fontList(mockRequest('/v1/fonts.json'));
37
- o(res.status).equals(200);
38
- o(res.body).equals('[]');
39
- o(res.header('etag')).notEquals(undefined);
40
- o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
41
- });
42
-
43
- o('should return 404 if no assets defined', async () => {
44
- delete process.env[Env.AssetLocation];
27
+ o('should return 404 if no font found', async () => {
45
28
  const res = await fontList(mockRequest('/v1/fonts.json'));
46
29
  o(res.status).equals(404);
47
30
  });
48
31
 
49
32
  o('should return a list of fonts found', async () => {
50
- await Promise.all([
51
- fsa.write('memory://fonts/Roboto Thin/0-255.pbf', Buffer.from('')),
52
- fsa.write('memory://fonts/Roboto Thin/256-512.pbf', Buffer.from('')),
53
- fsa.write('memory://fonts/Roboto Black/0-255.pbf', Buffer.from('')),
54
- ]);
33
+ await fsa.write('memory://fonts.json', Buffer.from(JSON.stringify(['Roboto Black', 'Roboto Thin'])));
55
34
  const res = await fontList(mockRequest('/v1/fonts.json'));
56
35
  o(res.status).equals(200);
57
36
  o(res.header('content-type')).equals('application/json');
58
37
  o(res.header('content-encoding')).equals(undefined);
59
- o(res.body).equals(JSON.stringify(['Roboto Black', 'Roboto Thin']));
38
+ o(res._body?.toString()).equals(JSON.stringify(['Roboto Black', 'Roboto Thin']));
60
39
  });
61
40
 
62
41
  o('should get the correct font', async () => {
@@ -83,4 +62,10 @@ o.spec('/v1/fonts', () => {
83
62
  o(res255.header('etag')).notEquals(undefined);
84
63
  o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
85
64
  });
65
+
66
+ o('should return 404 if no asset location set', async () => {
67
+ assetProvider.set(undefined);
68
+ const res = await fontList(mockRequest('/v1/fonts.json'));
69
+ o(res.status).equals(404);
70
+ });
86
71
  });
@@ -1,26 +1,31 @@
1
1
  import { Env } from '@basemaps/shared';
2
2
  import { fsa } from '@chunkd/fs';
3
3
  import o from 'ospec';
4
+ import { createSandbox } from 'sinon';
4
5
  import { gunzipSync, gzipSync } from 'zlib';
5
6
  import { handler } from '../../index.js';
7
+ import { assetProvider } from '../../util/assets.provider.js';
6
8
  import { mockRequest } from '../../__tests__/xyz.util.js';
7
9
  import { FsMemory } from './memory.fs.js';
8
10
 
9
11
  o.spec('/v1/sprites', () => {
10
12
  const memory = new FsMemory();
13
+ const sandbox = createSandbox();
11
14
  o.before(() => {
12
15
  fsa.register('memory://', memory);
13
16
  });
17
+ const assetLocation = process.env[Env.AssetLocation];
14
18
 
15
19
  o.beforeEach(() => {
16
20
  process.env[Env.AssetLocation] = 'memory://';
21
+ assetProvider.set('memory://');
17
22
  });
18
23
 
19
24
  o.afterEach(() => {
20
- delete process.env[Env.AssetLocation];
25
+ assetProvider.set(assetLocation);
21
26
  memory.files.clear();
27
+ sandbox.restore();
22
28
  });
23
-
24
29
  o('should return 404 if no assets defined', async () => {
25
30
  delete process.env[Env.AssetLocation];
26
31
  const res404 = await handler.router.handle(mockRequest('/v1/sprites/topographic.json'));
@@ -63,7 +63,8 @@ o.spec('/v1/tiles', () => {
63
63
 
64
64
  ['png', 'webp', 'jpeg', 'avif'].forEach((fmt) => {
65
65
  o(`should 200 with empty ${fmt} if a tile is out of bounds`, async () => {
66
- // tiler.tile = async () => [];
66
+ o.timeout(1_000);
67
+
67
68
  const res = await handler.router.handle(
68
69
  mockRequest(`/v1/tiles/aerial/global-mercator/0/0/0.${fmt}`, 'get', Api.header),
69
70
  );
@@ -71,7 +72,6 @@ o.spec('/v1/tiles', () => {
71
72
  o(res.header('content-type')).equals(`image/${fmt}`);
72
73
  o(res.header('etag')).notEquals(undefined);
73
74
  o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
74
- // o(rasterMock.calls.length).equals(1);
75
75
  });
76
76
  });
77
77
 
@@ -1,76 +1,16 @@
1
- import { Env } from '@basemaps/shared';
2
- import { fsa } from '@chunkd/fs';
3
- import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
1
+ import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
2
  import path from 'path';
5
- import { isGzip, serveFromCotar } from '../util/cotar.serve.js';
6
- import { Etag } from '../util/etag.js';
7
- import { NotFound, NotModified } from '../util/response.js';
3
+ import { assetProvider } from '../util/assets.provider.js';
8
4
 
9
5
  interface FontGet {
10
6
  Params: { fontStack: string; range: string };
11
7
  }
12
8
 
13
9
  export async function fontGet(req: LambdaHttpRequest<FontGet>): Promise<LambdaHttpResponse> {
14
- const assetLocation = Env.get(Env.AssetLocation);
15
- if (assetLocation == null) return NotFound();
16
-
17
10
  const targetFile = path.join('fonts', req.params.fontStack, req.params.range) + '.pbf';
18
- if (assetLocation.endsWith('.tar.co')) {
19
- return serveFromCotar(req, assetLocation, targetFile, 'application/x-protobuf');
20
- }
21
-
22
- try {
23
- const filePath = fsa.join(assetLocation, targetFile);
24
- const buf = await fsa.read(filePath);
25
-
26
- const cacheKey = Etag.key(buf);
27
- if (Etag.isNotModified(req, cacheKey)) return NotModified();
28
-
29
- const response = LambdaHttpResponse.ok().buffer(buf, 'application/x-protobuf');
30
- response.header(HttpHeader.ETag, cacheKey);
31
- response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
32
- if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
33
- return response;
34
- } catch (e: any) {
35
- if (e.code === 404) return NotFound();
36
- throw e;
37
- }
38
- }
39
-
40
- /** Get the unique name of folders is a path that contain .pbf files */
41
- export async function getFonts(fontPath: string): Promise<string[]> {
42
- const fonts = new Set<string>();
43
-
44
- // TODO use {recursive: false}
45
- for await (const font of fsa.list(fontPath)) {
46
- if (!font.endsWith('.pbf')) continue;
47
- const dirName = path.basename(path.dirname(font)); // TODO this only works for /a/b.pbf and not /a/b/c.pbf
48
- if (dirName.includes('/')) continue;
49
- fonts.add(dirName);
50
- }
51
- // Ensure the fonts are alphabetical
52
- return [...fonts].sort();
11
+ return assetProvider.serve(req, targetFile, 'application/x-protobuf');
53
12
  }
54
13
 
55
14
  export async function fontList(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
56
- const assetLocation = Env.get(Env.AssetLocation);
57
- if (assetLocation == null) return NotFound();
58
-
59
- if (assetLocation.endsWith('.tar.co')) return serveFromCotar(req, assetLocation, 'fonts.json', 'application/json');
60
-
61
- try {
62
- const filePath = fsa.join(assetLocation, '/fonts');
63
- const fonts = await getFonts(filePath);
64
-
65
- const cacheKey = Etag.key(fonts);
66
- if (Etag.isNotModified(req, cacheKey)) return NotModified();
67
-
68
- const response = LambdaHttpResponse.ok().buffer(JSON.stringify(fonts), 'application/json');
69
- response.header(HttpHeader.ETag, cacheKey);
70
- response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
71
- return response;
72
- } catch (e: any) {
73
- if (e.code === 404) return NotFound();
74
- throw e;
75
- }
15
+ return assetProvider.serve(req, 'fonts.json', 'application/json');
76
16
  }
@@ -1,10 +1,8 @@
1
- import { Env } from '@basemaps/shared';
2
1
  import { fsa } from '@chunkd/fs';
3
2
  import path from 'path';
4
- import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
- import { NotFound, NotModified } from '../util/response.js';
6
- import { isGzip, serveFromCotar } from '../util/cotar.serve.js';
7
- import { Etag } from '../util/etag.js';
3
+ import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+ import { NotFound } from '../util/response.js';
5
+ import { assetProvider } from '../util/assets.provider.js';
8
6
 
9
7
  interface SpriteGet {
10
8
  Params: {
@@ -17,31 +15,10 @@ Extensions.set('.png', 'image/png');
17
15
  Extensions.set('.json', 'application/json');
18
16
 
19
17
  export async function spriteGet(req: LambdaHttpRequest<SpriteGet>): Promise<LambdaHttpResponse> {
20
- const assetLocation = Env.get(Env.AssetLocation);
21
- if (assetLocation == null) return NotFound();
22
-
23
18
  const extension = path.extname(req.params.spriteName);
24
19
  const mimeType = Extensions.get(extension);
25
20
  if (mimeType == null) return NotFound();
26
21
 
27
22
  const targetFile = fsa.join('sprites', req.params.spriteName);
28
- if (assetLocation.endsWith('.tar.co')) return serveFromCotar(req, assetLocation, targetFile, mimeType);
29
-
30
- try {
31
- const filePath = fsa.join(assetLocation, targetFile);
32
- req.set('target', filePath);
33
-
34
- const buf = await fsa.read(filePath);
35
- const cacheKey = Etag.key(buf);
36
- if (Etag.isNotModified(req, cacheKey)) return NotModified();
37
-
38
- const response = LambdaHttpResponse.ok().buffer(buf, mimeType);
39
- response.header(HttpHeader.ETag, cacheKey);
40
- response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
41
- if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
42
- return response;
43
- } catch (e: any) {
44
- if (e.code === 404) return NotFound();
45
- throw e;
46
- }
23
+ return assetProvider.serve(req, targetFile, mimeType);
47
24
  }
@@ -3,7 +3,7 @@ import { GoogleTms, VectorFormat } from '@basemaps/geo';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import { isGzip } from '../util/cotar.serve.js';
5
5
  import { Etag } from '../util/etag.js';
6
- import { NotFound, NotModified } from '../util/response.js';
6
+ import { NoContent, NotFound, NotModified } from '../util/response.js';
7
7
  import { CoSources } from '../util/source.cache.js';
8
8
  import { TileXyz } from '../util/validate.js';
9
9
 
@@ -34,7 +34,7 @@ export const tileXyzVector = {
34
34
 
35
35
  req.timer.start('cotar:tile');
36
36
  const tile = await cotar.get(tilePath);
37
- if (tile == null) return NotFound();
37
+ if (tile == null) return NoContent();
38
38
  req.timer.end('cotar:tile');
39
39
 
40
40
  const tileBuffer = Buffer.from(tile);
@@ -0,0 +1,67 @@
1
+ import { fsa } from '@chunkd/fs';
2
+ import { LambdaHttpResponse, LambdaHttpRequest, HttpHeader } from '@linzjs/lambda';
3
+ import { isGzip } from './cotar.serve.js';
4
+ import { Etag } from './etag.js';
5
+ import { NotFound, NotModified } from './response.js';
6
+ import { CoSources } from './source.cache.js';
7
+
8
+ export class AssetProvider {
9
+ /**
10
+ * Assets can be ready from the following locations.
11
+ *
12
+ * /home/blacha/config/build/assets # Local File
13
+ * /home/blacha/config/build/assets.tar.co # Local Cotar
14
+ * s3://linz-baesmaps/assets/ # Remote location
15
+ * s3://linz-basemaps/assets/assets-b4ff211a.tar.co # Remote Cotar
16
+ */
17
+
18
+ /** Path of the assets location */
19
+ path: string | undefined;
20
+
21
+ set(path?: string): void {
22
+ this.path = path;
23
+ }
24
+
25
+ async get(fileName: string): Promise<Buffer | null> {
26
+ if (this.path == null) return null;
27
+ // get assets file from cotar
28
+ if (this.path.endsWith('.tar.co')) return await this.getFromCotar(this.path, fileName);
29
+
30
+ // get assets file for directory
31
+ try {
32
+ const filePath = fsa.join(this.path, fileName);
33
+ return await fsa.read(filePath);
34
+ } catch (e: any) {
35
+ if (e.code === 404) return null;
36
+ throw e;
37
+ }
38
+ }
39
+
40
+ async getFromCotar(path: string, fileName: string): Promise<Buffer | null> {
41
+ const cotar = await CoSources.getCotar(path);
42
+ const data = await cotar.get(fileName);
43
+ return data ? Buffer.from(data) : data;
44
+ }
45
+
46
+ /**
47
+ * Load a assets from local path or cotar returning the file back as a LambdaResponse
48
+ *
49
+ * This will also set two headers
50
+ * - Content-Encoding if the file starts with gzip magic
51
+ * - Content-Type from the parameter contentType
52
+ */
53
+ async serve(req: LambdaHttpRequest, file: string, contentType: string): Promise<LambdaHttpResponse> {
54
+ const buf = await assetProvider.get(file);
55
+ if (buf == null) return NotFound();
56
+ const cacheKey = Etag.key(buf);
57
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
58
+
59
+ const response = LambdaHttpResponse.ok().buffer(buf, contentType);
60
+ response.header(HttpHeader.ETag, cacheKey);
61
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
62
+ if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
63
+ return response;
64
+ }
65
+ }
66
+
67
+ export const assetProvider = new AssetProvider();
@@ -0,0 +1,44 @@
1
+ import { ConfigBundled, ConfigProviderMemory } from '@basemaps/config';
2
+ import { fsa } from '@chunkd/fs';
3
+ import { SwappingLru } from './swapping.lru.js';
4
+
5
+ class LruConfig {
6
+ configProvider: Promise<ConfigProviderMemory>;
7
+
8
+ constructor(config: Promise<ConfigBundled>) {
9
+ this.configProvider = config.then((c) => {
10
+ const configProvider = ConfigProviderMemory.fromJson(c);
11
+ configProvider.createVirtualTileSets();
12
+ return configProvider;
13
+ });
14
+ }
15
+
16
+ get size(): number {
17
+ // Return size 1 for the config and cache the number of configs based on size number.
18
+ return 1;
19
+ }
20
+ }
21
+
22
+ export class ConfigCache {
23
+ cache: SwappingLru<LruConfig>;
24
+ constructor(maxSize: number) {
25
+ this.cache = new SwappingLru<LruConfig>(maxSize);
26
+ }
27
+
28
+ getConfig(location: string): Promise<ConfigProviderMemory | null> {
29
+ const existing = this.cache.get(location)?.configProvider;
30
+ if (existing != null) return existing;
31
+ try {
32
+ const configJson = fsa.readJson<ConfigBundled>(location);
33
+ const config = new LruConfig(configJson);
34
+ this.cache.set(location, config);
35
+ return config.configProvider;
36
+ } catch (e: any) {
37
+ if (e.code === 404) return Promise.resolve(null);
38
+ throw e;
39
+ }
40
+ }
41
+ }
42
+
43
+ /** Cache 20 configs(Around 500KB each)*/
44
+ export const CachedConfig = new ConfigCache(20);
@@ -1,4 +1,7 @@
1
- import { LambdaHttpResponse } from '@linzjs/lambda';
1
+ import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
2
2
 
3
3
  export const NotFound = (): LambdaHttpResponse => new LambdaHttpResponse(404, 'Not Found');
4
4
  export const NotModified = (): LambdaHttpResponse => new LambdaHttpResponse(304, 'Not modified');
5
+ export const NoContent = (): LambdaHttpResponse => new LambdaHttpResponse(204, 'No Content');
6
+ export const OkResponse = (req: LambdaHttpRequest): LambdaHttpResponse =>
7
+ new LambdaHttpResponse(200, 'ok').json({ id: req.id, correlationId: req.correlationId, message: 'ok' });
@@ -68,4 +68,5 @@ export class SourceCache {
68
68
  }
69
69
  }
70
70
 
71
- export const CoSources = new SourceCache(256 * 1024 * 1024);
71
+ /** Approx ~1.4GB 2x~700MB caches */
72
+ export const CoSources = new SourceCache(700 * 1000 * 1000);
@@ -6,6 +6,7 @@ export class SwappingLru<T extends { size: number }> {
6
6
  hits = 0;
7
7
  misses = 0;
8
8
  resets = 0;
9
+ clears = 0;
9
10
 
10
11
  _lastCheckedAt = -1;
11
12
 
@@ -37,6 +38,7 @@ export class SwappingLru<T extends { size: number }> {
37
38
  clear(): void {
38
39
  this.cacheA.clear();
39
40
  this.cacheB.clear();
41
+ this.clears++;
40
42
  }
41
43
 
42
44
  set(id: string, tiff: T): void {
@@ -50,6 +52,11 @@ export class SwappingLru<T extends { size: number }> {
50
52
  if (this.maxSize <= 0) return;
51
53
  if (this.currentSize <= this.maxSize) return;
52
54
  this.resets++;
55
+ // Paranoia if we are resetting too often something is wrong, reset the entire cache.
56
+ if (this.resets > 100) {
57
+ this.resets = 0;
58
+ return this.clear();
59
+ }
53
60
  this.cacheB = this.cacheA;
54
61
  this.cacheA = new Map();
55
62
  }
@@ -1,8 +1,7 @@
1
1
  import { ImageFormat, TileMatrixSet, TileMatrixSets, VectorFormat } from '@basemaps/geo';
2
- import { Const, Projection } from '@basemaps/shared';
2
+ import { Const, isValidApiKey, Projection } from '@basemaps/shared';
3
3
  import { getImageFormat } from '@basemaps/tiler';
4
4
  import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
- import * as ulid from 'ulid';
6
5
  import { TileXyzGet } from '../routes/tile.xyz';
7
6
 
8
7
  export interface TileXyz {
@@ -16,31 +15,6 @@ export interface TileMatrixRequest {
16
15
  Params: { tileMatrix?: string };
17
16
  }
18
17
 
19
- const OneHourMs = 60 * 60 * 1000;
20
- const OneDayMs = 24 * OneHourMs;
21
- const MaxApiAgeMs = 91 * OneDayMs;
22
-
23
- export interface ApiKeyStatus {
24
- valid: boolean;
25
- message: 'ok' | 'malformed' | 'missing' | 'expired';
26
- }
27
-
28
- export function isValidApiKey(apiKey?: string | null): ApiKeyStatus {
29
- if (apiKey == null) return { valid: false, message: 'missing' };
30
- if (!apiKey.startsWith('c') && !apiKey.startsWith('d')) return { valid: false, message: 'malformed' };
31
- const ulidId = apiKey.slice(1).toUpperCase();
32
- try {
33
- const ulidTime = ulid.decodeTime(ulidId);
34
- if (apiKey.startsWith('d')) return { valid: true, message: 'ok' };
35
-
36
- if (Date.now() - ulidTime > MaxApiAgeMs) return { valid: false, message: 'expired' };
37
- } catch (e) {
38
- return { valid: false, message: 'malformed' };
39
- }
40
-
41
- return { valid: true, message: 'ok' };
42
- }
43
-
44
18
  export const Validate = {
45
19
  /**
46
20
  * Validate that the api key exists and is valid
package/tsconfig.json CHANGED
@@ -6,6 +6,7 @@
6
6
  },
7
7
  "include": ["src/**/*"],
8
8
  "references": [
9
+ { "path": "../config" },
9
10
  { "path": "../shared" },
10
11
  { "path": "../geo" },
11
12
  { "path": "../tiler" },