@nyaitter/nap 0.0.0-stage → 0.2.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/README.md +141 -2
- package/cli.js +157 -0
- package/client/README.md +135 -0
- package/client/nap-client.js +613 -0
- package/client/nap-page.js +187 -0
- package/client/package.json +1 -0
- package/lib/core.js +166 -0
- package/lib/declaration.js +89 -0
- package/lib/fork.js +147 -0
- package/lib/get.js +209 -0
- package/lib/git.js +39 -0
- package/lib/glob.js +59 -0
- package/lib/init.js +53 -0
- package/lib/inspect.js +120 -0
- package/lib/publish.js +772 -0
- package/lib/read.js +68 -0
- package/lib/remove.js +70 -0
- package/lib/search.js +92 -0
- package/package.json +17 -4
package/README.md
CHANGED
|
@@ -1,3 +1,142 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NAP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
リポジトリに版を付けて公開し、公開された版を取り寄せるためのツールです。
|
|
4
|
+
|
|
5
|
+
## インストール
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @nyaitter/nap
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
npx からも使えます。
|
|
12
|
+
|
|
13
|
+
```console
|
|
14
|
+
$ npx @nyaitter/nap publish
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 使い始める
|
|
18
|
+
|
|
19
|
+
### 版を作る
|
|
20
|
+
|
|
21
|
+
リポジトリのルートに nap.json を作ります。
|
|
22
|
+
|
|
23
|
+
```console
|
|
24
|
+
$ nap init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
nap.json は次の形です。
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"name": "nyaitter-client",
|
|
32
|
+
"include": ["**"],
|
|
33
|
+
"composition": ["nyaitter/nyaitter.js", "nyaitter/nyaitterui", "nyaitter/nyaitterime"]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
発行します。
|
|
38
|
+
|
|
39
|
+
```console
|
|
40
|
+
$ nap publish
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
今のコミットの内容が版になります。同じ内容で再実行しても、新しい版は作られません。
|
|
44
|
+
|
|
45
|
+
指定できるもの:
|
|
46
|
+
|
|
47
|
+
- --ref <コミット|ブランチ>: 対象のコミットを変える
|
|
48
|
+
- --remote <リモート>: 公開先を変える。既定は origin
|
|
49
|
+
- --libraries <file.json>: 一緒に使うパッケージの入手先を渡す
|
|
50
|
+
- --test <ラベル>: 開発中の版として発行する
|
|
51
|
+
- --no-push: 版を作るだけで、公開しない
|
|
52
|
+
- --message <説明>: 版の説明を付ける。説明は nap show で見られる
|
|
53
|
+
|
|
54
|
+
--libraries のファイルは次の形です。版を固定したいときは version に版IDを書きます。
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"nyaitter/nyaitter.js": { "repo": "Nyaitter/Nyaitter.js", "source": "../nyaitter.js" },
|
|
59
|
+
"nyaitter/nyaitterui": { "repo": "Nyaitter/NyaitterUI", "source": "../nyaitter-ui" }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 版を取り寄せる
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
$ nap get Nyaitter/Client
|
|
67
|
+
$ nap get Nyaitter/Client/<版ID>
|
|
68
|
+
$ nap get Nyaitter/Client/<版コード>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
指定がなければ、最新の正式版です。取り寄せた内容は照合してから置きます。置き場所は --out <ディレクトリ> で指定できます。既定はリポジトリ名のフォルダです。一緒に使うパッケージも一緒に置かれます。
|
|
72
|
+
|
|
73
|
+
### プロジェクトへ導入する
|
|
74
|
+
|
|
75
|
+
nap.json のあるフォルダで、--out を付けずに実行すると導入になります。
|
|
76
|
+
|
|
77
|
+
```console
|
|
78
|
+
$ nap get Nyaitter/Nyaitter.js
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
パッケージは nap_modules の中に置かれ、版まで nap.json の composition に記録されます。導入したパッケージは相対パスでそのまま使えます。nap_modules は Git の追跡対象にしません。
|
|
82
|
+
|
|
83
|
+
nap get を指定なしで実行すると、記録されたものをすべて導入します。
|
|
84
|
+
|
|
85
|
+
発行するときは、記録されたパッケージが版に含まれます。版は単独で使えます。
|
|
86
|
+
|
|
87
|
+
## 版を調べる
|
|
88
|
+
|
|
89
|
+
```console
|
|
90
|
+
$ nap list
|
|
91
|
+
$ nap show <版ID>
|
|
92
|
+
$ nap show <版コード>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
nap list は版の一覧を出します。nap show は、指定がなければ最新の正式版を出します。版IDは先頭だけでも指定できます。
|
|
96
|
+
|
|
97
|
+
## パッケージを探す
|
|
98
|
+
|
|
99
|
+
```console
|
|
100
|
+
$ nap search client
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
nap.json を持つリポジトリを探します。GITHUB_TOKEN の設定が必要です。
|
|
104
|
+
|
|
105
|
+
## フォーク
|
|
106
|
+
|
|
107
|
+
```console
|
|
108
|
+
$ nap fork Nyaitter/Nyaitter.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
パッケージを手元で直したいときは nap fork を使います。元のリポジトリを複製したものが nap_modules の中に置かれ、普通の Git リポジトリとして編集できます。nap get では上書きされません。
|
|
112
|
+
|
|
113
|
+
ルートで nap publish を実行すると、フォークの今の内容が版に含まれます。フォークに変更があれば、一緒に発行するか確認します。
|
|
114
|
+
|
|
115
|
+
## 取り除く
|
|
116
|
+
|
|
117
|
+
```console
|
|
118
|
+
$ nap remove Nyaitter/Nyaitter.js
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
導入したパッケージと nap.json の記録を取り除きます。対象がフォークのときは、作業内容が失われるため確認します。--yes を付けると確認を省けます。
|
|
122
|
+
|
|
123
|
+
## 開発中の版
|
|
124
|
+
|
|
125
|
+
開発中の版は TestVersion と呼びます。
|
|
126
|
+
|
|
127
|
+
```console
|
|
128
|
+
$ nap publish --test <ラベル>
|
|
129
|
+
$ nap get Nyaitter/Client/<版ID>-<ラベル>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
誰でも取得して試せますが、最新の版としては選ばれません。同じ内容を --test なしで発行すると、そのまま正式版になります。
|
|
133
|
+
|
|
134
|
+
## 非公開の版
|
|
135
|
+
|
|
136
|
+
非公開の版はありません。正式に出す前に試してもらいたいときは、開発中の版を使ってください。版そのものを非公開にしたいときは、プライベートリポジトリなど、置き場所側の仕組みを使ってください。
|
|
137
|
+
|
|
138
|
+
## ドキュメント
|
|
139
|
+
|
|
140
|
+
- [仕様](./SPEC.md)
|
|
141
|
+
- [取得ライブラリ](./client/README.md)
|
|
142
|
+
- [配信サーバー](./cdn/README.md)
|
package/cli.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { publish } = require('./lib/publish');
|
|
5
|
+
const { list, show } = require('./lib/inspect');
|
|
6
|
+
const { get } = require('./lib/get');
|
|
7
|
+
const { search } = require('./lib/search');
|
|
8
|
+
|
|
9
|
+
function usage() {
|
|
10
|
+
console.error('使い方:');
|
|
11
|
+
console.error(' nap publish [<リポジトリ>] [--ref <コミット|ブランチ>] [--remote <リモート>] [--libraries <file.json>] [--test <ラベル>] [--message <説明>] [--no-push]\n nap init [<名前>]');
|
|
12
|
+
console.error(' nap list [<リポジトリ>] [--remote <リモート>]');
|
|
13
|
+
console.error(' nap show [<版ID>] [--repo <リポジトリ>] [--remote <リモート>]');
|
|
14
|
+
console.error(' nap get [<指定>] [--out <出力先>]');
|
|
15
|
+
console.error(' nap fork <指定>\n nap remove <指定> [--yes]');
|
|
16
|
+
console.error(' nap search [キーワード...] [--limit <件数>]');
|
|
17
|
+
console.error('publish は、リポジトリの中で実行するとそのリポジトリを対象にします。');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseOptions(args, allowed) {
|
|
21
|
+
const positional = [];
|
|
22
|
+
const options = {};
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
const arg = args[i];
|
|
25
|
+
if (arg.startsWith('--')) {
|
|
26
|
+
const key = arg.slice(2);
|
|
27
|
+
if (!allowed.includes(key)) return { error: `不明なオプションです: ${arg}` };
|
|
28
|
+
const value = args[++i];
|
|
29
|
+
if (value === undefined) return { error: `オプションには値が必要です: ${arg}` };
|
|
30
|
+
options[key] = value;
|
|
31
|
+
} else {
|
|
32
|
+
positional.push(arg);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { positional, options };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main(argv) {
|
|
39
|
+
const [command, ...rest] = argv;
|
|
40
|
+
if (command === 'publish') {
|
|
41
|
+
const noPush = rest.includes('--no-push');
|
|
42
|
+
const publishArgs = rest.filter((a) => a !== '--no-push');
|
|
43
|
+
const { positional, options, error } = parseOptions(publishArgs, [
|
|
44
|
+
'ref',
|
|
45
|
+
'remote',
|
|
46
|
+
'libraries',
|
|
47
|
+
'chunk-size',
|
|
48
|
+
'test',
|
|
49
|
+
'message',
|
|
50
|
+
]);
|
|
51
|
+
if (error || positional.length > 1) {
|
|
52
|
+
if (error) console.error(error);
|
|
53
|
+
usage();
|
|
54
|
+
return 2;
|
|
55
|
+
}
|
|
56
|
+
const chunkSize =
|
|
57
|
+
options['chunk-size'] !== undefined
|
|
58
|
+
? Number(options['chunk-size'])
|
|
59
|
+
: Number(process.env.NAP_CHUNK_SIZE || 200);
|
|
60
|
+
await publish(positional[0], {
|
|
61
|
+
ref: options.ref,
|
|
62
|
+
remote: options.remote,
|
|
63
|
+
libraries: options.libraries,
|
|
64
|
+
chunkSize,
|
|
65
|
+
test: options.test,
|
|
66
|
+
message: options.message,
|
|
67
|
+
noPush,
|
|
68
|
+
});
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
if (command === 'list') {
|
|
72
|
+
const { positional, options, error } = parseOptions(rest, ['remote']);
|
|
73
|
+
if (error || positional.length > 1) {
|
|
74
|
+
if (error) console.error(error);
|
|
75
|
+
usage();
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
await list(positional[0], { remote: options.remote });
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
if (command === 'show') {
|
|
82
|
+
const { positional, options, error } = parseOptions(rest, ['repo', 'remote']);
|
|
83
|
+
if (error || positional.length > 1) {
|
|
84
|
+
if (error) console.error(error);
|
|
85
|
+
usage();
|
|
86
|
+
return 2;
|
|
87
|
+
}
|
|
88
|
+
await show(positional[0], { repo: options.repo, remote: options.remote });
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
if (command === 'init') {
|
|
92
|
+
if (rest.length > 1 || (rest.length === 1 && rest[0].startsWith('-'))) {
|
|
93
|
+
usage();
|
|
94
|
+
return 2;
|
|
95
|
+
}
|
|
96
|
+
const { init } = require('./lib/init');
|
|
97
|
+
await init(rest[0]);
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (command === 'get') {
|
|
102
|
+
const { positional, options, error } = parseOptions(rest, ['out']);
|
|
103
|
+
if (error || positional.length > 1) {
|
|
104
|
+
if (error) console.error(error);
|
|
105
|
+
usage();
|
|
106
|
+
return 2;
|
|
107
|
+
}
|
|
108
|
+
await get(positional[0], { out: options.out });
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
if (command === 'fork') {
|
|
112
|
+
const { fork } = require('./lib/fork');
|
|
113
|
+
const { positional, options, error } = parseOptions(rest, []);
|
|
114
|
+
if (error || positional.length !== 1 || Object.keys(options).length > 0) {
|
|
115
|
+
if (error) console.error(error);
|
|
116
|
+
usage();
|
|
117
|
+
return 2;
|
|
118
|
+
}
|
|
119
|
+
await fork(positional[0]);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
if (command === 'remove') {
|
|
123
|
+
const yes = rest.includes('--yes');
|
|
124
|
+
const removeArgs = rest.filter((a) => a !== '--yes');
|
|
125
|
+
if (removeArgs.length !== 1) {
|
|
126
|
+
usage();
|
|
127
|
+
return 2;
|
|
128
|
+
}
|
|
129
|
+
const { remove } = require('./lib/remove');
|
|
130
|
+
await remove(removeArgs[0], { yes });
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (command === 'search') {
|
|
135
|
+
const { positional, options, error } = parseOptions(rest, ['limit']);
|
|
136
|
+
if (error) {
|
|
137
|
+
console.error(error);
|
|
138
|
+
usage();
|
|
139
|
+
return 2;
|
|
140
|
+
}
|
|
141
|
+
const limit = options.limit !== undefined ? Number(options.limit) : 20;
|
|
142
|
+
await search(positional, { limit });
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
usage();
|
|
146
|
+
return 2;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
main(process.argv.slice(2)).then(
|
|
150
|
+
(code) => {
|
|
151
|
+
process.exitCode = code;
|
|
152
|
+
},
|
|
153
|
+
(error) => {
|
|
154
|
+
console.error(error instanceof Error ? error.message : error);
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
},
|
|
157
|
+
);
|
package/client/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# nap-client
|
|
2
|
+
|
|
3
|
+
NAP の版を取得するライブラリです。単一ファイルで、追加のパッケージは要りません。ブラウザと Node の両方で使えます。
|
|
4
|
+
|
|
5
|
+
## createClient
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import { createClient, createMemoryCache } from './nap-client.js';
|
|
9
|
+
|
|
10
|
+
const client = createClient({
|
|
11
|
+
cache: createMemoryCache(),
|
|
12
|
+
});
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
指定できるもの:
|
|
16
|
+
|
|
17
|
+
- rawBase: 取得先の基底 URL。nap-cdn のような配信サーバーを使うときに指定する
|
|
18
|
+
- cache: get と put を持つオブジェクト。省略するとメモリ上のものになる
|
|
19
|
+
- fetch: 取得に使う fetch。省略すると標準の fetch を使う
|
|
20
|
+
|
|
21
|
+
取得先は、各呼び出しに渡す入手先から決まります。入手先の形は所有者とリポジトリ名を並べたもので、対象は GitHub のリポジトリです。
|
|
22
|
+
|
|
23
|
+
## listVersions
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
const { total, entries } = await client.listVersions(repo, { latestOnly: true });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
版の一覧を返します。戻り値は total と entries です。entries は発行順で、各件は id と commit と at と tag を持ちます。tag は正式版では null、開発中の版ではそのラベルです。開発中の版は TestVersion と呼びます。
|
|
30
|
+
|
|
31
|
+
latestOnly に true を指定すると、返る entries は最後の一まとまり分だけになります。total は全体の件数のままです。latestOnly を指定しないときは、全件が返ります。
|
|
32
|
+
|
|
33
|
+
## latestVersion
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const latest = await client.latestVersion(repo);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
最新の正式版を1件返します。形は id と commit と at と tag です。tag は null です。開発中の版は対象になりません。正式版が無ければ null を返します。
|
|
40
|
+
|
|
41
|
+
## getManifest
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const manifest = await client.getManifest(repo, ref);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
版のマニフェストを返します。ref は版ID、または版IDとラベルを並べた形です。ラベル付きは開発中の版を指します。内容が版IDと合わなければ例外になります。
|
|
48
|
+
|
|
49
|
+
## getManifests
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
const manifests = await client.getManifests(repo, ref);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
マニフェストだけを、構成員まで含めて返します。ファイルの中身は取得しません。戻り値は versionId と tag と files と libraries で、libraries の各値も同じ形です。
|
|
56
|
+
|
|
57
|
+
## fetchFile
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
const bytes = await client.fetchFile(repo, ref, path);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
版に属するファイル1点を取得し、バイト列を返します。照合は行わないので、呼び出し側でマニフェストと照合してください。
|
|
64
|
+
|
|
65
|
+
## parseSpec と fetchSpec と getSpecManifests
|
|
66
|
+
|
|
67
|
+
パッケージは3つの要素で指定します。所有者とリポジトリと版です。
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const parsed = parseSpec('Nyaitter/Client/<版ID>');
|
|
71
|
+
const version = await client.fetchSpec('Nyaitter/Client/<版ID>');
|
|
72
|
+
const manifests = await client.getSpecManifests('Nyaitter/Client/<版コード>');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
parseSpec は指定を repo と ref と id と tag に分けます。対象は版IDだけです。fetchSpec と getSpecManifests では、版の部分に版コードも使えます。版コードは発行のときに付く短いコードです。fetchSpec は fetchVersion と同じものを返し、getSpecManifests は getManifests と同じものを返します。
|
|
76
|
+
|
|
77
|
+
## fetchVersion
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
const version = await client.fetchVersion(repo, ref);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
版のファイルすべてを取得します。ref は版ID、または版IDとラベルを並べた形です。開発中の版を使うときはラベルまで書きます。
|
|
84
|
+
|
|
85
|
+
第3引数に onProgress を渡すと、取得の進み具合が phase と fetched と cached と total の形で通知されます。phase は download と unpack のどちらかです。
|
|
86
|
+
|
|
87
|
+
rawBase を渡したクライアントは、配信サーバーの専用 API で版の一覧を取り、版のファイルは ZIP で一括取得します。ZIP の取得や展開に失敗したときは、ファイルごとの取得に切り替わります。ZIP の取得中は、進捗の bytes と bytesTotal に受信バイト数と全体のバイト数が入ります。
|
|
88
|
+
|
|
89
|
+
戻り値は versionId と tag と files と libraries と stats です。
|
|
90
|
+
|
|
91
|
+
- versionId: 版ID。ラベルは含まない
|
|
92
|
+
- tag: 正式版では null、開発中の版ではそのラベル
|
|
93
|
+
- files: パスとバイト列の対応。バイト列は Uint8Array
|
|
94
|
+
- libraries: 構成員ごとの、同じ形の戻り値
|
|
95
|
+
- stats: fetched と cached と total。取得した件数と、手元で済んだ件数と、全体の件数
|
|
96
|
+
|
|
97
|
+
取得したファイルはマニフェストと照合します。照合が1つでも失敗したら例外になり、一部だけの結果は返りません。構成員も同じように照合します。
|
|
98
|
+
|
|
99
|
+
## NAP-Page
|
|
100
|
+
|
|
101
|
+
nap-page.js は、ページを NAP の版で管理するライブラリです。更新の確認と、版の取得と、ブラウザへの保存をします。
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
import { createNapPage } from './nap-page.js';
|
|
105
|
+
|
|
106
|
+
const page = createNapPage({
|
|
107
|
+
source: '<所有者>/<リポジトリ>',
|
|
108
|
+
rawBase: 'https://cdn.nyaitter.jp',
|
|
109
|
+
currentVersionId: '<今の版ID>',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const { hasUpdate, latest } = await page.checkUpdate();
|
|
113
|
+
if (hasUpdate) {
|
|
114
|
+
const result = await page.fetchVersion(latest.id);
|
|
115
|
+
await page.store(result);
|
|
116
|
+
await page.activate(latest.id);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
source は必須です。rawBase と currentVersionId は任意です。rawBase の既定は https://cdn.nyaitter.jp です。
|
|
121
|
+
|
|
122
|
+
使える呼び出し:
|
|
123
|
+
|
|
124
|
+
- versions: 版の一覧を返す
|
|
125
|
+
- latest: 最新の正式版を返す
|
|
126
|
+
- checkUpdate: 更新があるかと最新の正式版を返す
|
|
127
|
+
- fetchVersion: 版を取得する
|
|
128
|
+
- describe: 版の説明を返す。取得できなければ空
|
|
129
|
+
- fetchLatest: 最新の正式版を取得する
|
|
130
|
+
- store: 取得した版をブラウザに保存する
|
|
131
|
+
- storedVersions: 保存済みの版を返す
|
|
132
|
+
- activate: 指定した版を有効にし、他の版の保存を消す
|
|
133
|
+
- cacheName: 版の保存名を返す
|
|
134
|
+
|
|
135
|
+
store はブラウザの Cache Storage を使います。使えない環境ではエラーになります。
|