chapterjs 0.3.0 → 0.4.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 (48) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/build.js +14 -1
  3. package/dist/cli/dev.js +29 -1
  4. package/dist/cli/env.d.ts +5 -0
  5. package/dist/cli/env.js +39 -3
  6. package/dist/cli/project.d.ts +30 -7
  7. package/dist/cli/project.js +85 -11
  8. package/dist/cli/redis.d.ts +23 -0
  9. package/dist/cli/redis.js +57 -0
  10. package/dist/cli/start.js +43 -17
  11. package/dist/cli/test.js +4 -2
  12. package/dist/events/registry.d.ts +252 -239
  13. package/dist/events/registry.js +125 -100
  14. package/dist/index.d.ts +4 -2
  15. package/dist/index.js +1 -0
  16. package/dist/ranking/declaration.d.ts +12 -0
  17. package/dist/ranking/declaration.js +48 -0
  18. package/dist/ranking/ranking.d.ts +102 -0
  19. package/dist/ranking/ranking.js +129 -0
  20. package/dist/redis/client.d.ts +36 -0
  21. package/dist/redis/client.js +252 -0
  22. package/dist/redis/protocol.d.ts +22 -0
  23. package/dist/redis/protocol.js +99 -0
  24. package/dist/redis/url.d.ts +24 -0
  25. package/dist/redis/url.js +50 -0
  26. package/dist/store/backend.d.ts +49 -4
  27. package/dist/store/backend.js +66 -11
  28. package/dist/store/declaration.d.ts +3 -0
  29. package/dist/store/declaration.js +8 -3
  30. package/dist/store/file-backend.d.ts +3 -1
  31. package/dist/store/file-backend.js +29 -0
  32. package/dist/store/ipc-backend.d.ts +24 -1
  33. package/dist/store/ipc-backend.js +49 -0
  34. package/dist/store/memory-backend.d.ts +4 -2
  35. package/dist/store/memory-backend.js +27 -1
  36. package/dist/store/ranking-backend.d.ts +45 -0
  37. package/dist/store/ranking-backend.js +82 -0
  38. package/dist/store/redis-backend.d.ts +38 -0
  39. package/dist/store/redis-backend.js +245 -0
  40. package/dist/store/redis-ranking.d.ts +15 -0
  41. package/dist/store/redis-ranking.js +69 -0
  42. package/dist/store/scope.d.ts +11 -0
  43. package/dist/store/scope.js +23 -0
  44. package/dist/store/store.d.ts +39 -5
  45. package/dist/store/store.js +79 -32
  46. package/dist/structures/invite.d.ts +6 -0
  47. package/dist/structures/invite.js +11 -0
  48. package/package.json +1 -1
package/dist/cli/start.js CHANGED
@@ -2,6 +2,8 @@
2
2
  // Nothing is reloaded, commands are registered for every server, and a
3
3
  // large bot is spread over several processes by itself.
4
4
  import { IpcStoreBackend } from '../store/ipc-backend.js';
5
+ import { NowhereToKeepError } from '../store/backend.js';
6
+ import { checkRedis, describeRedisStores, redisStores } from './redis.js';
5
7
  import { registerCommands } from '../commands/register.js';
6
8
  import { buildCommands } from '../commands/tree.js';
7
9
  import { GLOBAL_REQUESTS_PER_SECOND } from '../discord/api.js';
@@ -13,7 +15,7 @@ import { connectToPrimary, planProcesses, REFUSED, runCluster, } from './cluster
13
15
  import { loadBundle, readBuild } from './build.js';
14
16
  import { readStartEnv } from './env.js';
15
17
  import { describeIntents, ensurePrivilegedIntents, fetchApplication, } from './preflight.js';
16
- import { createProject, explain } from './project.js';
18
+ import { createProject, explain, reportLooseRejections, } from './project.js';
17
19
  /** Runs until stopped. Resolves with the exit code of the process. */
18
20
  export async function start(options) {
19
21
  const { cwd, log, signal } = options;
@@ -49,25 +51,30 @@ export async function start(options) {
49
51
  }
50
52
  return 1;
51
53
  }
54
+ // Every process reaches Redis itself: it is made to be shared.
55
+ const redis = read.env.redis ? redisStores(read.env.redis, log) : null;
52
56
  const project = createProject({
53
57
  cwd,
54
58
  version: options.version,
55
59
  log,
56
60
  built: files,
57
61
  joinsVoice: built.info.voice === true,
58
- // A process started by another keeps no data: the first one does, for
59
- // all of them.
60
- ...(assignment
61
- ? {
62
- stores: new IpcStoreBackend({
63
- send: message => {
64
- if (process.connected)
65
- process.send?.(message);
66
- },
67
- on: listener => process.on('message', listener),
68
- }),
69
- }
70
- : {}),
62
+ stores: {
63
+ // A process started by another keeps no file: the first one does,
64
+ // for all of them.
65
+ ...(assignment
66
+ ? {
67
+ file: new IpcStoreBackend({
68
+ send: message => {
69
+ if (process.connected)
70
+ process.send?.(message);
71
+ },
72
+ on: listener => process.on('message', listener),
73
+ }),
74
+ }
75
+ : {}),
76
+ ...(redis ? { redis } : {}),
77
+ },
71
78
  ...(options.deferAfter === undefined
72
79
  ? {}
73
80
  : { deferAfter: options.deferAfter }),
@@ -77,7 +84,12 @@ export async function start(options) {
77
84
  for (const failure of failures)
78
85
  project.report(failure);
79
86
  if (!assignment) {
80
- log.error(`${failures.length === 1 ? 'This file' : `These ${failures.length} files`} of the build can't run any more, so the bot was not started. Run "chapterjs build" again: it says what to fix.`);
87
+ // A store kept in Redis without REDIS_URL is about the environment,
88
+ // not the build.
89
+ const unkept = failures.every(failure => failure.error instanceof NowhereToKeepError);
90
+ log.error(unkept
91
+ ? 'The bot was not started. Give REDIS_URL to the bot the way your host does it (its "environment variables" or "secrets"), or in a .env file in this folder.'
92
+ : `${failures.length === 1 ? 'This file' : `These ${failures.length} files`} of the build can't run any more, so the bot was not started. Run "chapterjs build" again: it says what to fix.`);
81
93
  }
82
94
  return 1;
83
95
  }
@@ -94,10 +106,18 @@ export async function start(options) {
94
106
  ? { guildFilter: (id) => id !== devGuildId }
95
107
  : {}),
96
108
  };
109
+ // A promise of the files left alone is an error of the file, not the end
110
+ // of the bot: in every process, since every one runs the files.
111
+ const loose = reportLooseRejections(cwd, log);
97
112
  try {
98
113
  if (assignment)
99
114
  return await runShare(project, options, connection, assignment);
100
- // 3. What Discord must agree with, checked once for every process.
115
+ // 3. What Redis, then Discord, must agree with, checked once for every
116
+ // process.
117
+ if (!(await checkRedis(project, redis, log))) {
118
+ await project.closeStores();
119
+ return 1;
120
+ }
101
121
  const rest = new RestClient({
102
122
  token,
103
123
  version: options.version,
@@ -132,6 +152,9 @@ export async function start(options) {
132
152
  log.success(`Online as ${user} in ${guilds} ${guilds === 1 ? 'server' : 'servers'}${split ? ` (${split})` : ''}`);
133
153
  log.info(`Intents computed from your files: ${describeIntents(intents)}`);
134
154
  (project.isEmpty() ? log.info : log.success)(project.summary());
155
+ const kept = describeRedisStores(project, redis);
156
+ if (kept)
157
+ log.info(kept);
135
158
  };
136
159
  if (processes === 1) {
137
160
  return await runAlone(project, options, connection, online);
@@ -146,7 +169,7 @@ export async function start(options) {
146
169
  signal,
147
170
  identifyInterval,
148
171
  write: options.write,
149
- stores: project.storeBackend,
172
+ stores: project.storeBackends.get('file'),
150
173
  onReady: ({ user, guilds }) => online(user, guilds),
151
174
  onRestart: (index, why) => log.warn(`Process ${index + 1} of ${processes} stopped by itself (${why}): starting it again. Its servers are back in a moment.`),
152
175
  });
@@ -162,6 +185,9 @@ export async function start(options) {
162
185
  explain(error, log);
163
186
  return assignment ? REFUSED : 1;
164
187
  }
188
+ finally {
189
+ loose();
190
+ }
165
191
  }
166
192
  /** Runs a bot until it is stopped or Discord refuses it. */
167
193
  async function run(project, connection, stopped, connected, { tasks = true } = {}) {
package/dist/cli/test.js CHANGED
@@ -67,7 +67,7 @@ function where(error, cwd) {
67
67
  }
68
68
  /** Runs the whole project in a fresh world; `close` ends it. */
69
69
  async function startSession(project, observer, stores) {
70
- stores.clear();
70
+ stores.reset();
71
71
  observer.reset();
72
72
  const world = new World();
73
73
  await world.start();
@@ -179,7 +179,9 @@ async function run(options, src, output) {
179
179
  cwd,
180
180
  version: options.version,
181
181
  log: capture.log,
182
- stores,
182
+ // Every store, wherever it says it keeps its data, is in memory here:
183
+ // a test never needs a Redis, and starts from nothing.
184
+ stores: { file: stores, redis: stores },
183
185
  observe: observer.observe,
184
186
  });
185
187
  await writeTypes(cwd);