Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { HawkStorage } from './storages/hawk-storage';
26 changes: 26 additions & 0 deletions packages/core/src/storages/hawk-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Abstract key–value storage contract used by Hawk internals to persist data across sessions.
*/
export interface HawkStorage {
/**
* Returns the value associated with the given key, or `null` if none exists.
*
* @param key - Storage key to look up.
*/
getItem(key: string): string | null;

/**
* Persists a value under the given key.
*
* @param key - Storage key.
* @param value - Value to store.
*/
setItem(key: string, value: string): void;

/**
* Removes the entry for the given key.
*
* @param key - Storage key to remove.
*/
removeItem(key: string): void;
}
3 changes: 3 additions & 0 deletions packages/javascript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build": "vite build",
"stats": "size-limit > stats.txt",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:watch": "vitest",
"lint": "eslint --fix \"src/**/*.{js,ts}\""
},
Expand All @@ -39,10 +40,12 @@
},
"homepage": "https://github.com/codex-team/hawk.javascript#readme",
"dependencies": {
"@hawk.so/core": "workspace:^",
"error-stack-parser": "^2.1.4"
},
"devDependencies": {
"@hawk.so/types": "0.5.8",
"@vitest/coverage-v8": "^4.0.18",
"jsdom": "^28.0.0",
"vite": "^7.3.1",
"vite-plugin-dts": "^4.2.4",
Expand Down
21 changes: 21 additions & 0 deletions packages/javascript/src/storages/hawk-local-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HawkStorage } from '@hawk.so/core';

/**
* {@link HawkStorage} implementation backed by the browser's {@linkcode localStorage}.
*/
export class HawkLocalStorage implements HawkStorage {
/** @inheritDoc */
public getItem(key: string): string | null {
return localStorage.getItem(key);
}

/** @inheritDoc */
public setItem(key: string, value: string): void {
localStorage.setItem(key, value);
}

/** @inheritDoc */
public removeItem(key: string): void {
localStorage.removeItem(key);
}
}
57 changes: 57 additions & 0 deletions packages/javascript/tests/storages/hawk-local-storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Mock } from 'vitest';
import { beforeEach, afterEach, describe, it, expect, vi } from 'vitest';
import { HawkLocalStorage } from '../../src/storages/hawk-local-storage';

describe('HawkLocalStorage', () => {
let getItemSpy: Mock<(key: string) => string | null>;
let setItemSpy: Mock<(key: string, value: string) => void>;
let removeItemSpy: Mock<(key: string) => void>;
let storage: HawkLocalStorage;

beforeEach(() => {
localStorage.clear();
storage = new HawkLocalStorage();
getItemSpy = vi.spyOn(Storage.prototype, 'getItem');
setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
removeItemSpy = vi.spyOn(Storage.prototype, 'removeItem');
});

afterEach(() => {
vi.restoreAllMocks();
});

it('should return null when key does not exist', () => {
expect(storage.getItem('foo')).toBeNull();
expect(getItemSpy).toHaveBeenCalledOnce();
});

it('should return value when key exists in storage', () => {
localStorage.setItem('foo', 'bar');

expect(storage.getItem('foo')).toEqual('bar');
expect(getItemSpy).toHaveBeenCalledWith('foo');
});

it('should persist item via setItem()', () => {
storage.setItem('foo', 'bar');

expect(setItemSpy).toHaveBeenCalledWith('foo', 'bar');
expect(localStorage.getItem('foo')).toEqual('bar');
});

it('should remove item via removeItem()', () => {
localStorage.setItem('foo', 'bar');
storage.removeItem('foo');

expect(removeItemSpy).toHaveBeenCalledWith('foo');
expect(localStorage.getItem('foo')).toBeNull();
});

it('should not affect other keys when removing', () => {
localStorage.setItem('foo', 'bar');
storage.removeItem('baz');

expect(removeItemSpy).toHaveBeenCalledWith('baz');
expect(localStorage.getItem('foo')).toEqual('bar');
});
});
3 changes: 2 additions & 1 deletion packages/javascript/tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": null,
"declaration": false
"declaration": false,
"types": ["vitest/globals"]
},
"include": [
"src/**/*",
Expand Down
112 changes: 109 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.28.5":
"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.28.5, @babel/parser@npm:^7.29.0":
version: 7.29.0
resolution: "@babel/parser@npm:7.29.0"
dependencies:
Expand All @@ -101,6 +101,13 @@ __metadata:
languageName: node
linkType: hard

"@bcoe/v8-coverage@npm:^1.0.2":
version: 1.0.2
resolution: "@bcoe/v8-coverage@npm:1.0.2"
checksum: 10c0/1eb1dc93cc17fb7abdcef21a6e7b867d6aa99a7ec88ec8207402b23d9083ab22a8011213f04b2cf26d535f1d22dc26139b7929e6c2134c254bd1e14ba5e678c3
languageName: node
linkType: hard

"@csstools/color-helpers@npm:^6.0.1":
version: 6.0.1
resolution: "@csstools/color-helpers@npm:6.0.1"
Expand Down Expand Up @@ -576,7 +583,7 @@ __metadata:
languageName: node
linkType: hard

"@hawk.so/core@workspace:packages/core":
"@hawk.so/core@workspace:^, @hawk.so/core@workspace:packages/core":
version: 0.0.0-use.local
resolution: "@hawk.so/core@workspace:packages/core"
dependencies:
Expand All @@ -589,7 +596,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@hawk.so/javascript@workspace:packages/javascript"
dependencies:
"@hawk.so/core": "workspace:^"
"@hawk.so/types": "npm:0.5.8"
"@vitest/coverage-v8": "npm:^4.0.18"
error-stack-parser: "npm:^2.1.4"
jsdom: "npm:^28.0.0"
vite: "npm:^7.3.1"
Expand Down Expand Up @@ -708,7 +717,7 @@ __metadata:
languageName: node
linkType: hard

"@jridgewell/trace-mapping@npm:^0.3.24":
"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.31":
version: 0.3.31
resolution: "@jridgewell/trace-mapping@npm:0.3.31"
dependencies:
Expand Down Expand Up @@ -1385,6 +1394,30 @@ __metadata:
languageName: node
linkType: hard

"@vitest/coverage-v8@npm:^4.0.18":
version: 4.0.18
resolution: "@vitest/coverage-v8@npm:4.0.18"
dependencies:
"@bcoe/v8-coverage": "npm:^1.0.2"
"@vitest/utils": "npm:4.0.18"
ast-v8-to-istanbul: "npm:^0.3.10"
istanbul-lib-coverage: "npm:^3.2.2"
istanbul-lib-report: "npm:^3.0.1"
istanbul-reports: "npm:^3.2.0"
magicast: "npm:^0.5.1"
obug: "npm:^2.1.1"
std-env: "npm:^3.10.0"
tinyrainbow: "npm:^3.0.3"
peerDependencies:
"@vitest/browser": 4.0.18
vitest: 4.0.18
peerDependenciesMeta:
"@vitest/browser":
optional: true
checksum: 10c0/e23e0da86f0b2a020c51562bc40ebdc7fc7553c24f8071dfb39a6df0161badbd5eaf2eebbf8ceaef18933a18c1934ff52d1c0c4bde77bb87e0c1feb0c8cbee4d
languageName: node
linkType: hard

"@vitest/expect@npm:4.0.18":
version: 4.0.18
resolution: "@vitest/expect@npm:4.0.18"
Expand Down Expand Up @@ -1846,6 +1879,17 @@ __metadata:
languageName: node
linkType: hard

"ast-v8-to-istanbul@npm:^0.3.10":
version: 0.3.11
resolution: "ast-v8-to-istanbul@npm:0.3.11"
dependencies:
"@jridgewell/trace-mapping": "npm:^0.3.31"
estree-walker: "npm:^3.0.3"
js-tokens: "npm:^10.0.0"
checksum: 10c0/0667dcb5f42bd16f5d50b8687f3471f9b9d000ea7f8808c3cd0ddabc1ef7d5b1a61e19f498d5ca7b1285e6c185e11d0ae724c4f9291491b50b6340110ce63108
languageName: node
linkType: hard

"astral-regex@npm:^2.0.0":
version: 2.0.0
resolution: "astral-regex@npm:2.0.0"
Expand Down Expand Up @@ -3602,6 +3646,13 @@ __metadata:
languageName: node
linkType: hard

"html-escaper@npm:^2.0.0":
version: 2.0.2
resolution: "html-escaper@npm:2.0.2"
checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0
languageName: node
linkType: hard

"http-cache-semantics@npm:^4.1.1":
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
Expand Down Expand Up @@ -3997,6 +4048,34 @@ __metadata:
languageName: node
linkType: hard

"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2":
version: 3.2.2
resolution: "istanbul-lib-coverage@npm:3.2.2"
checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b
languageName: node
linkType: hard

"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1":
version: 3.0.1
resolution: "istanbul-lib-report@npm:3.0.1"
dependencies:
istanbul-lib-coverage: "npm:^3.0.0"
make-dir: "npm:^4.0.0"
supports-color: "npm:^7.1.0"
checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7
languageName: node
linkType: hard

"istanbul-reports@npm:^3.2.0":
version: 3.2.0
resolution: "istanbul-reports@npm:3.2.0"
dependencies:
html-escaper: "npm:^2.0.0"
istanbul-lib-report: "npm:^3.0.0"
checksum: 10c0/d596317cfd9c22e1394f22a8d8ba0303d2074fe2e971887b32d870e4b33f8464b10f8ccbe6847808f7db485f084eba09e6c2ed706b3a978e4b52f07085b8f9bc
languageName: node
linkType: hard

"jiti@npm:^2.4.2":
version: 2.6.1
resolution: "jiti@npm:2.6.1"
Expand All @@ -4013,6 +4092,13 @@ __metadata:
languageName: node
linkType: hard

"js-tokens@npm:^10.0.0":
version: 10.0.0
resolution: "js-tokens@npm:10.0.0"
checksum: 10c0/a93498747812ba3e0c8626f95f75ab29319f2a13613a0de9e610700405760931624433a0de59eb7c27ff8836e526768fb20783861b86ef89be96676f2c996b64
languageName: node
linkType: hard

"js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
Expand Down Expand Up @@ -4249,6 +4335,26 @@ __metadata:
languageName: node
linkType: hard

"magicast@npm:^0.5.1":
version: 0.5.2
resolution: "magicast@npm:0.5.2"
dependencies:
"@babel/parser": "npm:^7.29.0"
"@babel/types": "npm:^7.29.0"
source-map-js: "npm:^1.2.1"
checksum: 10c0/924af677643c5a0a7d6cdb3247c0eb96fa7611b2ba6a5e720d35d81c503d3d9f5948eb5227f80f90f82ea3e7d38cffd10bb988f3fc09020db428e14f26e960d7
languageName: node
linkType: hard

"make-dir@npm:^4.0.0":
version: 4.0.0
resolution: "make-dir@npm:4.0.0"
dependencies:
semver: "npm:^7.5.3"
checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68
languageName: node
linkType: hard

"make-fetch-happen@npm:^15.0.0":
version: 15.0.3
resolution: "make-fetch-happen@npm:15.0.3"
Expand Down