Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Web Storage #1700

Closed
wants to merge 1 commit into from
Closed

Implement Web Storage #1700

wants to merge 1 commit into from

Conversation

dsseng
Copy link
Contributor

@dsseng dsseng commented Feb 7, 2019

@kitsonk
Copy link
Contributor

kitsonk commented Feb 7, 2019

These need to align to what is in lib.dom.d.ts. There is a single global interface Storage and constructor function (window.Storage) which needs to be newable. Also sessionStorage instanceof Storage === true.

The same goes for localStorage. You are going to have to manage whether you persist the data internally without change the API. I would recommend exporting Storage, sessionStorage, and localStorage from storage and not new them up in globals.ts but rather new them up inside the module. For the localStorage you would when persist the changes.

With the values (and possibly the keys) I think we need some sort of runtime checking (or coercion), because people will expect it if they are using just JavaScript. For example, this works without throwing an error:

sessionStorage.setItem("foo", 1);

It will return always return a string of course. So I would say that you would want to simply do a String(value) when setting a value.

@ry at the moment we don't have a concept of window.onunload within Deno, and we are going to need something like that so that localStorage can be serialised and persisted to the local file system.

@aduh95
Copy link
Contributor

aduh95 commented Feb 12, 2019

sessionStorage is supposed to be readonly as per the specs. Should we try to have that in Deno as well?

@dsseng
Copy link
Contributor Author

dsseng commented Mar 16, 2019

@ry maybe transfer this to deno_std?

key(index: number): string {
return Object.keys(this._storage)[index];
}
getItem(key: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be revelant to return an empty string instead of an undefined value in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zekth I don't think so. Maybe user put "" there? undefined will let user see that it's... undefined!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both solution are good, it's more like a philosophy. Some APIs returns empty strings, others undefined as the return type is a string. Was just wondering.

Copy link
Contributor

@aduh95 aduh95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation is lacking support for property getter/setter/deleter (EG: window.sessionStorage['key']= 0 or delete window.sessionStorage['key']) as defined in the HTML specs. We need a Proxy to implement that.

Some other points also differ from the HTML spec, I have tried to list them in my review.

return Object.keys(this._storage)[index];
}
getItem(key: string): string {
return this._storage[key];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec says:

Returns the current value associated with the given key, or null if the given key does not exist in the list associated with the object.

Suggested change
return this._storage[key];
return this._storage[key] || null;

key(index: number): string {
return Object.keys(this._storage)[index];
}
getItem(key: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getItem(key: string): string {
getItem(key: string): string | null {

assertEqual(sessionStorage.key(0), "test");
assertEqual(sessionStorage.key(1), "test1");
sessionStorage.clear();
assertEqual(sessionStorage.key(0), undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEqual(sessionStorage.key(0), undefined);
assertEqual(sessionStorage.key(0), null);

assertEqual(sessionStorage.getItem("test"), "John Doe");
assertEqual(sessionStorage.length, 1);
sessionStorage.clear();
assertEqual(sessionStorage.getItem("test"), undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEqual(sessionStorage.getItem("test"), undefined);
assertEqual(sessionStorage.getItem("test"), null);

sessionStorage.setItem("test", "John Doe");
assertEqual(sessionStorage.getItem("test"), "John Doe");
sessionStorage.removeItem("test");
assertEqual(sessionStorage.getItem("test"), undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEqual(sessionStorage.getItem("test"), undefined);
assertEqual(sessionStorage.getItem("test"), null);

});

test(function removeItems() {
assertEqual(sessionStorage.getItem("test"), undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEqual(sessionStorage.getItem("test"), undefined);
assertEqual(sessionStorage.getItem("test"), null);

import { test, assertEqual } from "./test_util.ts";

test(function storeItems() {
assertEqual(sessionStorage.getItem("test"), undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEqual(sessionStorage.getItem("test"), undefined);
assertEqual(sessionStorage.getItem("test"), null);

private _storage: Record<string, string> = {};

get length(): number {
return Object.keys(this._storage).length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO using a Map instead of an Object would make this way more efficient.

@@ -96,4 +97,8 @@ export type TextDecoder = textEncoding.TextDecoder;

window.performance = new performanceUtil.Performance();

export type Storage = storage.Storage;
export type SessionStorage = storage.SessionStorage;
window.sessionStorage = new storage.SessionStorage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a readonly property.

@@ -96,4 +97,8 @@ export type TextDecoder = textEncoding.TextDecoder;

window.performance = new performanceUtil.Performance();

export type Storage = storage.Storage;
export type SessionStorage = storage.SessionStorage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to export this type? It is not part of the HTML spec.

@bartlomieju
Copy link
Member

@sh7dm will you update this PR?

@ry
Copy link
Member

ry commented Jul 23, 2019

I'm still interested in adding built-in web storage support, but this needs more work and has now grown stale. Thanks @sh7dm for getting the ball rolling, I think we'll refer back to this in the future.

@ry ry closed this Jul 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants