@meilisearch/cli 0.0.0-stage → 0.1.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 (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -2
  3. package/bin/run.js +15 -0
  4. package/dist/commands/add/documents.js +54 -0
  5. package/dist/commands/add/documents.js.map +1 -0
  6. package/dist/commands/add/indexes.js +39 -0
  7. package/dist/commands/add/indexes.js.map +1 -0
  8. package/dist/commands/add/keys.js +63 -0
  9. package/dist/commands/add/keys.js.map +1 -0
  10. package/dist/commands/add/projects.js +96 -0
  11. package/dist/commands/add/projects.js.map +1 -0
  12. package/dist/commands/auth/login.js +26 -0
  13. package/dist/commands/auth/login.js.map +1 -0
  14. package/dist/commands/auth/logout.js +33 -0
  15. package/dist/commands/auth/logout.js.map +1 -0
  16. package/dist/commands/auth/refresh.js +22 -0
  17. package/dist/commands/auth/refresh.js.map +1 -0
  18. package/dist/commands/auth/status.js +34 -0
  19. package/dist/commands/auth/status.js.map +1 -0
  20. package/dist/commands/cancel/tasks.js +24 -0
  21. package/dist/commands/cancel/tasks.js.map +1 -0
  22. package/dist/commands/config/list.js +18 -0
  23. package/dist/commands/config/list.js.map +1 -0
  24. package/dist/commands/config/set.js +48 -0
  25. package/dist/commands/config/set.js.map +1 -0
  26. package/dist/commands/config/sync-with-cloud.js +144 -0
  27. package/dist/commands/config/sync-with-cloud.js.map +1 -0
  28. package/dist/commands/config/unset.js +28 -0
  29. package/dist/commands/config/unset.js.map +1 -0
  30. package/dist/commands/config/update.js +53 -0
  31. package/dist/commands/config/update.js.map +1 -0
  32. package/dist/commands/delete/documents.js +81 -0
  33. package/dist/commands/delete/documents.js.map +1 -0
  34. package/dist/commands/delete/indexes.js +28 -0
  35. package/dist/commands/delete/indexes.js.map +1 -0
  36. package/dist/commands/delete/keys.js +28 -0
  37. package/dist/commands/delete/keys.js.map +1 -0
  38. package/dist/commands/delete/projects.js +40 -0
  39. package/dist/commands/delete/projects.js.map +1 -0
  40. package/dist/commands/delete/settings.js +28 -0
  41. package/dist/commands/delete/settings.js.map +1 -0
  42. package/dist/commands/delete/tasks.js +34 -0
  43. package/dist/commands/delete/tasks.js.map +1 -0
  44. package/dist/commands/export.js +67 -0
  45. package/dist/commands/export.js.map +1 -0
  46. package/dist/commands/facet-search.js +87 -0
  47. package/dist/commands/facet-search.js.map +1 -0
  48. package/dist/commands/get/batches.js +35 -0
  49. package/dist/commands/get/batches.js.map +1 -0
  50. package/dist/commands/get/documents.js +74 -0
  51. package/dist/commands/get/documents.js.map +1 -0
  52. package/dist/commands/get/health.js +17 -0
  53. package/dist/commands/get/health.js.map +1 -0
  54. package/dist/commands/get/indexes.js +33 -0
  55. package/dist/commands/get/indexes.js.map +1 -0
  56. package/dist/commands/get/keys.js +36 -0
  57. package/dist/commands/get/keys.js.map +1 -0
  58. package/dist/commands/get/projects.js +40 -0
  59. package/dist/commands/get/projects.js.map +1 -0
  60. package/dist/commands/get/settings.js +25 -0
  61. package/dist/commands/get/settings.js.map +1 -0
  62. package/dist/commands/get/stats.js +37 -0
  63. package/dist/commands/get/stats.js.map +1 -0
  64. package/dist/commands/get/tasks.js +35 -0
  65. package/dist/commands/get/tasks.js.map +1 -0
  66. package/dist/commands/get/version.js +16 -0
  67. package/dist/commands/get/version.js.map +1 -0
  68. package/dist/commands/multi-search.js +39 -0
  69. package/dist/commands/multi-search.js.map +1 -0
  70. package/dist/commands/schema.js +95 -0
  71. package/dist/commands/schema.js.map +1 -0
  72. package/dist/commands/search.js +164 -0
  73. package/dist/commands/search.js.map +1 -0
  74. package/dist/commands/similar.js +86 -0
  75. package/dist/commands/similar.js.map +1 -0
  76. package/dist/commands/teleport.js +472 -0
  77. package/dist/commands/teleport.js.map +1 -0
  78. package/dist/commands/update/documents.js +56 -0
  79. package/dist/commands/update/documents.js.map +1 -0
  80. package/dist/commands/update/indexes.js +44 -0
  81. package/dist/commands/update/indexes.js.map +1 -0
  82. package/dist/commands/update/keys.js +42 -0
  83. package/dist/commands/update/keys.js.map +1 -0
  84. package/dist/commands/update/projects.js +62 -0
  85. package/dist/commands/update/projects.js.map +1 -0
  86. package/dist/commands/update/settings.js +145 -0
  87. package/dist/commands/update/settings.js.map +1 -0
  88. package/dist/lib/analytics.js +135 -0
  89. package/dist/lib/analytics.js.map +1 -0
  90. package/dist/lib/api-naming.js +435 -0
  91. package/dist/lib/api-naming.js.map +1 -0
  92. package/dist/lib/base-command.js +318 -0
  93. package/dist/lib/base-command.js.map +1 -0
  94. package/dist/lib/clients/analytics.js +24 -0
  95. package/dist/lib/clients/analytics.js.map +1 -0
  96. package/dist/lib/clients/management/generated/client/client.gen.js +217 -0
  97. package/dist/lib/clients/management/generated/client/client.gen.js.map +1 -0
  98. package/dist/lib/clients/management/generated/client/index.js +7 -0
  99. package/dist/lib/clients/management/generated/client/index.js.map +1 -0
  100. package/dist/lib/clients/management/generated/client/types.gen.js +3 -0
  101. package/dist/lib/clients/management/generated/client/types.gen.js.map +1 -0
  102. package/dist/lib/clients/management/generated/client/utils.gen.js +229 -0
  103. package/dist/lib/clients/management/generated/client/utils.gen.js.map +1 -0
  104. package/dist/lib/clients/management/generated/client.gen.js +4 -0
  105. package/dist/lib/clients/management/generated/client.gen.js.map +1 -0
  106. package/dist/lib/clients/management/generated/core/auth.gen.js +15 -0
  107. package/dist/lib/clients/management/generated/core/auth.gen.js.map +1 -0
  108. package/dist/lib/clients/management/generated/core/bodySerializer.gen.js +58 -0
  109. package/dist/lib/clients/management/generated/core/bodySerializer.gen.js.map +1 -0
  110. package/dist/lib/clients/management/generated/core/params.gen.js +110 -0
  111. package/dist/lib/clients/management/generated/core/params.gen.js.map +1 -0
  112. package/dist/lib/clients/management/generated/core/pathSerializer.gen.js +107 -0
  113. package/dist/lib/clients/management/generated/core/pathSerializer.gen.js.map +1 -0
  114. package/dist/lib/clients/management/generated/core/queryKeySerializer.gen.js +93 -0
  115. package/dist/lib/clients/management/generated/core/queryKeySerializer.gen.js.map +1 -0
  116. package/dist/lib/clients/management/generated/core/serverSentEvents.gen.js +133 -0
  117. package/dist/lib/clients/management/generated/core/serverSentEvents.gen.js.map +1 -0
  118. package/dist/lib/clients/management/generated/core/types.gen.js +3 -0
  119. package/dist/lib/clients/management/generated/core/types.gen.js.map +1 -0
  120. package/dist/lib/clients/management/generated/core/utils.gen.js +88 -0
  121. package/dist/lib/clients/management/generated/core/utils.gen.js.map +1 -0
  122. package/dist/lib/clients/management/generated/index.js +3 -0
  123. package/dist/lib/clients/management/generated/index.js.map +1 -0
  124. package/dist/lib/clients/management/generated/sdk.gen.js +398 -0
  125. package/dist/lib/clients/management/generated/sdk.gen.js.map +1 -0
  126. package/dist/lib/clients/management/generated/types.gen.js +3 -0
  127. package/dist/lib/clients/management/generated/types.gen.js.map +1 -0
  128. package/dist/lib/clients/management.js +21 -0
  129. package/dist/lib/clients/management.js.map +1 -0
  130. package/dist/lib/clients/meilisearch.js +69 -0
  131. package/dist/lib/clients/meilisearch.js.map +1 -0
  132. package/dist/lib/clients/oauth.js +225 -0
  133. package/dist/lib/clients/oauth.js.map +1 -0
  134. package/dist/lib/clients/ping.js +26 -0
  135. package/dist/lib/clients/ping.js.map +1 -0
  136. package/dist/lib/clients/trace.js +69 -0
  137. package/dist/lib/clients/trace.js.map +1 -0
  138. package/dist/lib/cloud-sync.js +113 -0
  139. package/dist/lib/cloud-sync.js.map +1 -0
  140. package/dist/lib/config.js +322 -0
  141. package/dist/lib/config.js.map +1 -0
  142. package/dist/lib/errors.js +302 -0
  143. package/dist/lib/errors.js.map +1 -0
  144. package/dist/lib/help.js +37 -0
  145. package/dist/lib/help.js.map +1 -0
  146. package/dist/lib/input.js +76 -0
  147. package/dist/lib/input.js.map +1 -0
  148. package/dist/lib/login.js +57 -0
  149. package/dist/lib/login.js.map +1 -0
  150. package/dist/lib/output.js +39 -0
  151. package/dist/lib/output.js.map +1 -0
  152. package/dist/lib/pagination.js +52 -0
  153. package/dist/lib/pagination.js.map +1 -0
  154. package/dist/lib/session.js +149 -0
  155. package/dist/lib/session.js.map +1 -0
  156. package/dist/lib/task-filters.js +118 -0
  157. package/dist/lib/task-filters.js.map +1 -0
  158. package/dist/lib/tasks.js +64 -0
  159. package/dist/lib/tasks.js.map +1 -0
  160. package/dist/lib/terminal.js +168 -0
  161. package/dist/lib/terminal.js.map +1 -0
  162. package/dist/lib/values.js +48 -0
  163. package/dist/lib/values.js.map +1 -0
  164. package/oclif.manifest.json +10470 -0
  165. package/package.json +81 -3
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Root and topic help with two extra sections: the global flags of every
3
+ * command and the environment variables of docs/contract/config.md, so that
4
+ * `meili --help` alone documents how to configure the CLI.
5
+ */
6
+ import { Help } from "@oclif/core";
7
+ import { BaseCommand } from "./base-command.js";
8
+ import { ENV_VARS } from "./config.js";
9
+ export default class MeiliHelp extends Help {
10
+ formatRoot() {
11
+ const variables = ENV_VARS.map((variable) => [
12
+ variable.name,
13
+ variable.description,
14
+ ]);
15
+ return [
16
+ super.formatRoot(),
17
+ this.section("GLOBAL FLAGS", globalFlagRows()),
18
+ this.section("ENVIRONMENT VARIABLES", variables),
19
+ ].join("\n\n");
20
+ }
21
+ formatTopic(topic) {
22
+ // `super` ends with a blank line, and oclif prints the commands of the topic
23
+ // right after this: the section is joined to the trimmed text and keeps the
24
+ // one trailing newline that separates it from them.
25
+ const topicHelp = super.formatTopic(topic).trimEnd();
26
+ return `${topicHelp}\n\n${this.section("GLOBAL FLAGS", globalFlagRows())}\n`;
27
+ }
28
+ }
29
+ /** `-o, --output=<mode>` and its description, in the order of the contract table. */
30
+ function globalFlagRows() {
31
+ return Object.entries(BaseCommand.baseFlags).map(([name, flag]) => {
32
+ const short = flag.char === undefined ? "" : `-${flag.char}, `;
33
+ const value = flag.type === "option" ? `=${flag.helpValue}` : "";
34
+ return [`${short}--${name}${value}`, flag.description ?? ""];
35
+ });
36
+ }
37
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/lib/help.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAmB,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,IAAI;IAChC,UAAU;QACjB,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAoB,EAAE,CAAC;YAC7D,QAAQ,CAAC,IAAI;YACb,QAAQ,CAAC,WAAW;SACrB,CAAC,CAAC;QACH,OAAO;YACL,KAAK,CAAC,UAAU,EAAE;YAClB,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,SAAS,CAAC;SACjD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC;IAEkB,WAAW,CAAC,KAAuB;QACpD,6EAA6E;QAC7E,4EAA4E;QAC5E,oDAAoD;QACpD,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QACrD,OAAO,GAAG,SAAS,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC,IAAI,CAAC;IAC/E,CAAC;CACF;AAED,qFAAqF;AACrF,SAAS,cAAc;IACrB,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,EAAE,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { MeiliCliError } from "./errors.js";
3
+ let stdin;
4
+ /**
5
+ * Stdin to its end, without the trailing newline, read once per process: oclif
6
+ * parses the flags in `init` and again in `run`. Called only when a flag is
7
+ * given as `-`: a command never reads stdin otherwise (docs/contract/output.md).
8
+ */
9
+ export function readStdin() {
10
+ stdin ??= readFileSync(0, "utf8").replace(/\r?\n$/, "");
11
+ return stdin;
12
+ }
13
+ /**
14
+ * The JSON body of `--file` or `--data`, undefined when neither is given.
15
+ * Read and parsed before anything is sent: a file that cannot be read and text
16
+ * that is not JSON are `cli_input_invalid`, exit 2 (docs/contract/output.md).
17
+ */
18
+ export function readJsonBody(flags) {
19
+ const text = flags.data ?? (flags.file === undefined ? undefined : readBodyFile(flags.file));
20
+ if (text === undefined)
21
+ return undefined;
22
+ try {
23
+ return JSON.parse(text);
24
+ }
25
+ catch (cause) {
26
+ throw new MeiliCliError("cli_input_invalid", `${bodySource(flags)} is not valid JSON.`, {
27
+ cause,
28
+ });
29
+ }
30
+ }
31
+ /**
32
+ * The same body, for a route whose body is an object of named fields. A body
33
+ * that is not an object is `cli_input_invalid`, exit 2, before anything is sent.
34
+ */
35
+ export function readJsonObjectBody(flags) {
36
+ const body = readJsonBody(flags);
37
+ if (body === undefined)
38
+ return {};
39
+ if (!isJsonObject(body)) {
40
+ throw new MeiliCliError("cli_input_invalid", `${bodySource(flags)} must be a JSON object.`);
41
+ }
42
+ return body;
43
+ }
44
+ /**
45
+ * The body a command sends on a route whose body is an object of named
46
+ * fields: the JSON of `--file` or `--data` first, then `fields`, the fields
47
+ * its own flags define, which override the ones the body carries (rule 8 of
48
+ * docs/contract/commands.md). A field left undefined is dropped rather than
49
+ * spread: an absent flag would otherwise erase the field the body carries,
50
+ * since JSON.stringify leaves an undefined value out.
51
+ */
52
+ export function readBodyWithFields(flags, fields) {
53
+ return {
54
+ ...readJsonObjectBody(flags),
55
+ ...Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined)),
56
+ };
57
+ }
58
+ /** Which of the two flags carried the body, for the message of an error about it. */
59
+ function bodySource(flags) {
60
+ return flags.data === undefined ? `\`${flags.file}\`` : "--data";
61
+ }
62
+ function readBodyFile(path) {
63
+ if (path === "-")
64
+ return readStdin();
65
+ try {
66
+ return readFileSync(path, "utf8");
67
+ }
68
+ catch (cause) {
69
+ throw new MeiliCliError("cli_input_invalid", `Cannot read \`${path}\`.`, { cause });
70
+ }
71
+ }
72
+ /** Narrows a parsed body to a JSON object, which an array and a scalar are not. */
73
+ export function isJsonObject(value) {
74
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75
+ }
76
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/lib/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,IAAI,KAAyB,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,KAAK,KAAK,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC;AACf,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAA0B;IACrD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7F,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CAAC,mBAAmB,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,qBAAqB,EAAE;YACtF,KAAK;SACN,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA0B;IAC3D,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,aAAa,CAAC,mBAAmB,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAA0B,EAC1B,MAA+B;IAE/B,OAAO;QACL,GAAG,kBAAkB,CAAC,KAAK,CAAC;QAC5B,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;KACzF,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,SAAS,UAAU,CAAC,KAA0B;IAC5C,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,SAAS,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CAAC,mBAAmB,EAAE,iBAAiB,IAAI,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The browser login of `meili auth login`, which `meili teleport` runs too
3
+ * when nobody is logged in. See ADR 0009.
4
+ */
5
+ import { spawn } from "node:child_process";
6
+ import { identify } from "./analytics.js";
7
+ import { getApiV2Me } from "./clients/management/generated/sdk.gen.js";
8
+ import { createManagementClient } from "./clients/management.js";
9
+ import { logIn, SCOPES } from "./clients/oauth.js";
10
+ import { credentialsFilePath, resolveOauthClient, writeCredentialsFile, } from "./session.js";
11
+ /** How long the human has to approve before the login gives up. */
12
+ const TIMEOUT_MS = 300_000;
13
+ /**
14
+ * Runs the OAuth flow, writes the session to the credentials file, with the
15
+ * Cloud user the usage events use from now on (ADR 0016), and returns both.
16
+ * `say` writes a line for the human: an interactive command draws it in its margin.
17
+ */
18
+ export async function logInWithBrowser(env, browser, userAgent, say = (line) => process.stderr.write(`${line}\n`)) {
19
+ const client = resolveOauthClient(env);
20
+ const tokens = await logIn(client, {
21
+ timeoutMs: TIMEOUT_MS,
22
+ onAuthorizationUrl: (url) => {
23
+ say(`Approve the login for \`${SCOPES}\` at:`);
24
+ say(url);
25
+ if (browser)
26
+ openInBrowser(url);
27
+ },
28
+ });
29
+ const session = { ...client, ...tokens };
30
+ const userId = await identify(env, async () => {
31
+ const management = createManagementClient({
32
+ url: session.managementUrl,
33
+ token: session.accessToken,
34
+ userAgent,
35
+ });
36
+ return (await getApiV2Me({ client: management })).id;
37
+ });
38
+ if (userId !== undefined)
39
+ session.userId = userId;
40
+ const path = credentialsFilePath(env);
41
+ writeCredentialsFile(path, session);
42
+ return { path, session };
43
+ }
44
+ /** Hands the URL to the desktop. A failure is not one: the URL is on stderr. */
45
+ function openInBrowser(url) {
46
+ // An argument array and no shell, on every platform: a consent URL always
47
+ // carries `&`, which `cmd` would read as a command separator.
48
+ const [command, args] = process.platform === "darwin"
49
+ ? ["open", [url]]
50
+ : process.platform === "win32"
51
+ ? ["rundll32", ["url.dll,FileProtocolHandler", url]]
52
+ : ["xdg-open", [url]];
53
+ const child = spawn(command, args, { stdio: "ignore", detached: true });
54
+ child.on("error", () => { });
55
+ child.unref();
56
+ }
57
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/lib/login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAElB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,mEAAmE;AACnE,MAAM,UAAU,GAAG,OAAO,CAAC;AAE3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAQ,EACR,OAAgB,EAChB,SAAiB,EACjB,GAAG,GAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC;IAEzE,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;QACjC,SAAS,EAAE,UAAU;QACrB,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,GAAG,CAAC,2BAA2B,MAAM,QAAQ,CAAC,CAAC;YAC/C,GAAG,CAAC,GAAG,CAAC,CAAC;YACT,IAAI,OAAO;gBAAE,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;KACF,CAAC,CAAC;IACH,MAAM,OAAO,GAAY,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,UAAU,GAAG,sBAAsB,CAAC;YACxC,GAAG,EAAE,OAAO,CAAC,aAAa;YAC1B,KAAK,EAAE,OAAO,CAAC,WAAW;YAC1B,SAAS;SACV,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,CAAC,CAAC,CAAC;IACH,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAClD,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACtC,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,gFAAgF;AAChF,SAAS,aAAa,CAAC,GAAW;IAChC,0EAA0E;IAC1E,8DAA8D;IAC9D,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Output contract: the only module that writes to stdout. See docs/contract/output.md.
3
+ *
4
+ * This file, errors.ts and config.ts implement the contract. They change only
5
+ * through a contract pull request.
6
+ */
7
+ import { MeiliCliError } from "./errors.js";
8
+ export const OUTPUT_MODES = ["json", "ndjson"];
9
+ export function isOutputMode(value) {
10
+ return typeof value === "string" && OUTPUT_MODES.includes(value);
11
+ }
12
+ /** `--output` wins, then `MEILI_OUTPUT`, then `json`. */
13
+ export function resolveOutputMode(options) {
14
+ if (isOutputMode(options.flag))
15
+ return options.flag;
16
+ const fromEnv = options.env.MEILI_OUTPUT;
17
+ if (fromEnv !== undefined && fromEnv !== "") {
18
+ if (isOutputMode(fromEnv))
19
+ return fromEnv;
20
+ throw new MeiliCliError("cli_usage", `MEILI_OUTPUT must be one of ${OUTPUT_MODES.join(", ")}, got \`${fromEnv}\`.`);
21
+ }
22
+ return "json";
23
+ }
24
+ /** Formats a value for stdout in the selected mode. Pure, so tests can snapshot it. */
25
+ export function formatData(value, options) {
26
+ if (options.mode === "json") {
27
+ return `${options.stdoutIsTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value)}\n`;
28
+ }
29
+ if (options.supportsNdjson !== true) {
30
+ throw new MeiliCliError("cli_usage", "The `ndjson` output mode is only available on list commands. Use `--output json`.");
31
+ }
32
+ const items = Array.isArray(value) ? value : [value];
33
+ return items.map((item) => `${JSON.stringify(item)}\n`).join("");
34
+ }
35
+ /** Writes one result on stdout. Data only, nothing else, ever. */
36
+ export function writeData(value, options) {
37
+ process.stdout.write(formatData(value, options));
38
+ }
39
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAU,CAAC;AAIxD,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,YAAkC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC1F,CAAC;AAQD,yDAAyD;AACzD,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACzC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,IAAI,YAAY,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC1C,MAAM,IAAI,aAAa,CACrB,WAAW,EACX,+BAA+B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,KAAK,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAUD,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,OAAyB;IAClE,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;IAC7F,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,aAAa,CACrB,WAAW,EACX,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,OAAyB;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pagination for list routes: `--limit`, `--offset` or `--from`, and `--all`.
3
+ * See docs/contract/output.md, section Pagination.
4
+ */
5
+ /** The envelope fields that describe one page rather than the whole list. */
6
+ const PAGE_FIELDS = ["limit", "offset", "from"];
7
+ function numberField(page, name) {
8
+ const value = page[name];
9
+ return typeof value === "number" ? value : 0;
10
+ }
11
+ /** The query of the page after `page`, or undefined once `total` is reached. */
12
+ export function nextOffsetQuery(query, page) {
13
+ const offset = numberField(page, "offset") + page.results.length;
14
+ const done = page.results.length === 0 || offset >= numberField(page, "total");
15
+ return done ? undefined : { ...query, offset };
16
+ }
17
+ /**
18
+ * The query of the page after `page` on a cursor route: the API answers the
19
+ * `from` of the next page in `next`, and a `next` that is not a number, null
20
+ * on the last page, ends the walk.
21
+ */
22
+ export function nextFromQuery(query, page) {
23
+ const next = page.next;
24
+ return typeof next === "number" ? { ...query, from: next } : undefined;
25
+ }
26
+ /** The query of the page after `page` on a numbered route, or undefined past `totalPages`. */
27
+ export function nextPageQuery(query, page) {
28
+ const current = numberField(page, "page");
29
+ const done = page.results.length === 0 || current >= numberField(page, "totalPages");
30
+ return done ? undefined : { ...query, page: current + 1 };
31
+ }
32
+ /** Fetches page after page until `nextQuery` says there is no page left. */
33
+ export async function* iteratePages(fetchPage, query, nextQuery) {
34
+ let current = query;
35
+ while (current !== undefined) {
36
+ const page = await fetchPage(current);
37
+ yield page;
38
+ current = nextQuery(current, page);
39
+ }
40
+ }
41
+ /**
42
+ * The envelope of the last page with `results` replaced by every item and the
43
+ * fields that describe one page removed. The others, like `total`, stay. A
44
+ * route carries one pair or the other, so dropping all three is dropping its.
45
+ */
46
+ export function mergePages(pages) {
47
+ const last = { ...(pages.at(-1) ?? { results: [] }) };
48
+ for (const field of PAGE_FIELDS)
49
+ delete last[field];
50
+ return { ...last, results: pages.flatMap((page) => page.results) };
51
+ }
52
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/lib/pagination.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,6EAA6E;AAC7E,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAEhD,SAAS,WAAW,CAAC,IAAc,EAAE,IAAY;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,IAAc;IAChE,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,IAAc;IAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACzE,CAAC;AAOD,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAsB,KAAQ,EAAE,IAAc;IACzE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,IAAI,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACrF,OAAO,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY,CACjC,SAAmC,EACnC,KAAQ,EACR,SAAsD;IAEtD,IAAI,OAAO,GAAkB,KAAK,CAAC;IACnC,OAAO,OAAO,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAM,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,IAAI,CAAC;QACX,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAA0B;IACnD,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACtD,KAAK,MAAM,KAAK,IAAI,WAAW;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;AACrE,CAAC"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * The Management API session: the credentials file `meili auth login` writes,
3
+ * the resolution order of `docs/contract/config.md`, and the refresh that keeps
4
+ * an access token usable. See ADR 0009.
5
+ *
6
+ * It sits above `config.ts`, which stays a leaf, and below the commands.
7
+ */
8
+ import { rmSync } from "node:fs";
9
+ import { dirname, join } from "node:path";
10
+ import { refreshAccessToken } from "./clients/oauth.js";
11
+ import { configFilePath, DEFAULT_MANAGEMENT_CLIENT_ID, DEFAULT_MANAGEMENT_DASHBOARD_URL, DEFAULT_MANAGEMENT_URL, ENV, nonEmpty, readOwnerOnly, writeSecretFile, } from "./config.js";
12
+ import { isRecord, MeiliCliError } from "./errors.js";
13
+ /**
14
+ * A token travels in the clear to anything but HTTPS, so only a loopback
15
+ * address, where nothing leaves the machine, may be plain HTTP.
16
+ */
17
+ function requireSecureUrl(url, what) {
18
+ let parsed;
19
+ try {
20
+ parsed = new URL(url);
21
+ }
22
+ catch {
23
+ throw new MeiliCliError("cli_usage", `${what} is not a URL: ${url}`);
24
+ }
25
+ // `hostname` keeps the brackets of an IPv6 address and drops the port, `host` does not.
26
+ const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(parsed.hostname);
27
+ if (parsed.protocol !== "https:" && !loopback) {
28
+ throw new MeiliCliError("cli_usage", `${what} must be HTTPS, or a loopback address: ${url} would send the token in the clear.`);
29
+ }
30
+ }
31
+ /**
32
+ * The OAuth application `auth login` identifies itself with: the environment,
33
+ * then the application of this CLI, which every Cloud registers with the same id.
34
+ */
35
+ export function resolveOauthClient(env) {
36
+ // The two URLs name the same Cloud: one alone would send the human to the consent
37
+ // page of a Cloud and the code to the token endpoint of another.
38
+ const managementUrl = nonEmpty(env[ENV.MANAGEMENT_URL]);
39
+ const dashboardUrl = nonEmpty(env[ENV.MANAGEMENT_DASHBOARD_URL]);
40
+ if ((managementUrl === undefined) !== (dashboardUrl === undefined)) {
41
+ throw new MeiliCliError("cli_usage", "A Cloud is named by both of its URLs. Set `MEILI_MANAGEMENT_URL` and `MEILI_MANAGEMENT_DASHBOARD_URL` together, or neither to use Meilisearch Cloud.");
42
+ }
43
+ const client = {
44
+ clientId: nonEmpty(env[ENV.MANAGEMENT_CLIENT_ID]) ?? DEFAULT_MANAGEMENT_CLIENT_ID,
45
+ managementUrl: managementUrl ?? DEFAULT_MANAGEMENT_URL,
46
+ dashboardUrl: dashboardUrl ?? DEFAULT_MANAGEMENT_DASHBOARD_URL,
47
+ };
48
+ requireSecureUrl(client.managementUrl, ENV.MANAGEMENT_URL);
49
+ requireSecureUrl(client.dashboardUrl, ENV.MANAGEMENT_DASHBOARD_URL);
50
+ return client;
51
+ }
52
+ /** `credentials.json`, next to the config file: `MEILI_CONFIG` moves both. */
53
+ export function credentialsFilePath(env) {
54
+ return join(dirname(configFilePath(env)), "credentials.json");
55
+ }
56
+ /** The session of `auth login`, undefined when nobody logged in. */
57
+ export function readCredentialsFile(path, warn) {
58
+ const text = readOwnerOnly(path, "credentials", warn);
59
+ if (text === undefined)
60
+ return undefined;
61
+ const malformed = () => new MeiliCliError(`cli_config`, `The credentials file ${path} is not a session of \`meili auth login\`.`, { hint: "Run `meili auth login` to write it again." });
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(text);
65
+ }
66
+ catch {
67
+ throw malformed();
68
+ }
69
+ const fields = ["clientId", "managementUrl", "dashboardUrl", "accessToken", "expiresAt", "scope"];
70
+ if (!isRecord(parsed) || !fields.every((field) => typeof parsed[field] === "string")) {
71
+ throw malformed();
72
+ }
73
+ if (["refreshToken", "userId"].some((field) => parsed[field] !== undefined && typeof parsed[field] !== "string")) {
74
+ throw malformed();
75
+ }
76
+ const session = parsed;
77
+ requireSecureUrl(session.managementUrl, `the \`managementUrl\` of ${path}`);
78
+ return session;
79
+ }
80
+ /** Writes the session owner-only and atomically, like the config file. */
81
+ export function writeCredentialsFile(path, session) {
82
+ writeSecretFile(path, session);
83
+ }
84
+ /** Forgets the session. Does nothing when there is none. */
85
+ export function removeCredentialsFile(path) {
86
+ rmSync(path, { force: true });
87
+ }
88
+ /** A token with less than this left is refreshed before the command runs. */
89
+ const EXPIRY_MARGIN_MS = 30_000;
90
+ /**
91
+ * `MEILI_MANAGEMENT_TOKEN`, then the session of `auth login`, with
92
+ * `MEILI_MANAGEMENT_URL` and its default. Fails before any network call when
93
+ * none exists. An access token that is about to expire is refreshed here, and
94
+ * the new one is written back.
95
+ */
96
+ export async function resolveManagementCredentials(env, warn) {
97
+ const chosen = nonEmpty(env[ENV.MANAGEMENT_URL]);
98
+ const token = nonEmpty(env[ENV.MANAGEMENT_TOKEN]);
99
+ if (token !== undefined) {
100
+ if (chosen !== undefined)
101
+ requireSecureUrl(chosen, ENV.MANAGEMENT_URL);
102
+ return { token, url: chosen ?? DEFAULT_MANAGEMENT_URL };
103
+ }
104
+ const path = credentialsFilePath(env);
105
+ const session = readCredentialsFile(path, warn);
106
+ if (session === undefined) {
107
+ throw new MeiliCliError("cli_auth_missing", "No Management API credentials.", {
108
+ hint: "Run `meili auth login` to log in with a browser.",
109
+ });
110
+ }
111
+ if (chosen !== undefined && chosen !== session.managementUrl) {
112
+ throw new MeiliCliError("cli_config", `The session of \`meili auth login\` was issued by ${session.managementUrl}, but the Management API URL given is ${chosen}. A token is never sent to another Cloud.`, {
113
+ hint: "Run `meili auth login` with `MEILI_MANAGEMENT_URL` and `MEILI_MANAGEMENT_DASHBOARD_URL` naming that Cloud, or unset `MEILI_MANAGEMENT_URL`.",
114
+ });
115
+ }
116
+ const fresh = await ensureFreshSession(path, session);
117
+ return { token: fresh.accessToken, url: fresh.managementUrl };
118
+ }
119
+ /**
120
+ * The session, with a new access token when the stored one is spent. Writes the
121
+ * new pair back, so the next command finds it fresh.
122
+ */
123
+ /**
124
+ * Trades the refresh token for a new pair and writes it back, so the next
125
+ * command finds it fresh. A server that answers without a new refresh token
126
+ * keeps the one we have, rather than losing the session (RFC 6749, section 6).
127
+ */
128
+ export async function refreshSession(path, session) {
129
+ if (session.refreshToken === undefined) {
130
+ throw new MeiliCliError("cli_auth_failed", "The session carries no refresh token.", {
131
+ hint: "Run `meili auth login` to log in again.",
132
+ });
133
+ }
134
+ const tokens = await refreshAccessToken(session, session.refreshToken);
135
+ const refreshed = {
136
+ ...session,
137
+ ...tokens,
138
+ refreshToken: tokens.refreshToken ?? session.refreshToken,
139
+ };
140
+ writeCredentialsFile(path, refreshed);
141
+ return refreshed;
142
+ }
143
+ /** The session, refreshed only when its access token is spent. */
144
+ export function ensureFreshSession(path, session) {
145
+ if (Date.parse(session.expiresAt) - Date.now() > EXPIRY_MARGIN_MS)
146
+ return Promise.resolve(session);
147
+ return refreshSession(path, session);
148
+ }
149
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAuC,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EACL,cAAc,EACd,4BAA4B,EAC5B,gCAAgC,EAChC,sBAAsB,EACtB,GAAG,EAEH,QAAQ,EACR,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAOtD;;;GAGG;AACH,SAAS,gBAAgB,CAAC,GAAW,EAAE,IAAY;IACjD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,kBAAkB,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,wFAAwF;IACxF,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,aAAa,CACrB,WAAW,EACX,GAAG,IAAI,0CAA0C,GAAG,qCAAqC,CAC1F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAQ;IACzC,kFAAkF;IAClF,iEAAiE;IACjE,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,aAAa,CACrB,WAAW,EACX,sJAAsJ,CACvJ,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,IAAI,4BAA4B;QACjF,aAAa,EAAE,aAAa,IAAI,sBAAsB;QACtD,YAAY,EAAE,YAAY,IAAI,gCAAgC;KAC/D,CAAC;IACF,gBAAgB,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3D,gBAAgB,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACpE,OAAO,MAAM,CAAC;AAChB,CAAC;AAWD,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB,CAAC,GAAQ;IAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;AAChE,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,IAAsC;IAEtC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IACtD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,SAAS,GAAG,GAAkB,EAAE,CACpC,IAAI,aAAa,CACf,YAAY,EACZ,wBAAwB,IAAI,4CAA4C,EACxE,EAAE,IAAI,EAAE,2CAA2C,EAAE,CACtD,CAAC;IAEJ,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAClG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QACrF,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IACD,IACE,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,IAAI,CAC7B,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAC5E,EACD,CAAC;QACD,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IACD,MAAM,OAAO,GAAG,MAA4B,CAAC;IAC7C,gBAAgB,CAAC,OAAO,CAAC,aAAa,EAAE,4BAA4B,IAAI,EAAE,CAAC,CAAC;IAC5E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,OAAgB;IACjE,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,6EAA6E;AAC7E,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,GAAQ,EACR,IAAsC;IAEtC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,MAAM,KAAK,SAAS;YAAE,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;QACvE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,IAAI,sBAAsB,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,aAAa,CAAC,kBAAkB,EAAE,gCAAgC,EAAE;YAC5E,IAAI,EAAE,kDAAkD;SACzD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC;QAC7D,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,qDAAqD,OAAO,CAAC,aAAa,yCAAyC,MAAM,2CAA2C,EACpK;YACE,IAAI,EAAE,6IAA6I;SACpJ,CACF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,OAAgB;IACjE,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,aAAa,CAAC,iBAAiB,EAAE,uCAAuC,EAAE;YAClF,IAAI,EAAE,yCAAyC;SAChD,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACvE,MAAM,SAAS,GAAY;QACzB,GAAG,OAAO;QACV,GAAG,MAAM;QACT,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY;KAC1D,CAAC;IACF,oBAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,OAAgB;IAC/D,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB;QAC/D,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The selectors the task queue routes share: `GET /tasks`, `POST /tasks/cancel`,
3
+ * `DELETE /tasks` and `GET /batches` take the same query parameters, so the
4
+ * four commands spread the same flags and build the same query. Only the two
5
+ * identifier flags differ: named by what they identify, bare `uids` are task
6
+ * uids everywhere, so `get tasks` reads them as `--ids` and the batches as
7
+ * `--batch-ids`, and `get batches` the other way round
8
+ * (docs/contract/commands.md, rule 5). The API spelling stays an alias.
9
+ */
10
+ import { Flags } from "@oclif/core";
11
+ /** The selectors of the four routes but the two identifier flags of the resource. */
12
+ const queueFilterFlags = {
13
+ "index-ids": Flags.string({
14
+ aliases: ["index-uids"],
15
+ description: "Match the tasks of these indexes, comma-separated. `*` matches every one.",
16
+ helpValue: "<indexes>",
17
+ }),
18
+ "canceled-by": Flags.string({
19
+ description: "Match the tasks canceled by these taskCancelation uids, comma-separated. `*` matches every one.",
20
+ helpValue: "<uids>",
21
+ }),
22
+ types: Flags.string({
23
+ description: "Task types to match, comma-separated, as the `type` field of a task spells them, for example `documentAdditionOrUpdate`. `*` matches every one.",
24
+ helpValue: "<types>",
25
+ }),
26
+ statuses: Flags.string({
27
+ description: "Task statuses to match, comma-separated: enqueued, processing, succeeded, failed or canceled. `*` matches every one.",
28
+ helpValue: "<statuses>",
29
+ }),
30
+ "after-enqueued-at": Flags.string({
31
+ description: "Match the tasks enqueued after this date, in RFC 3339 format.",
32
+ helpValue: "<date>",
33
+ }),
34
+ "before-enqueued-at": Flags.string({
35
+ description: "Match the tasks enqueued before this date, in RFC 3339 format.",
36
+ helpValue: "<date>",
37
+ }),
38
+ "after-started-at": Flags.string({
39
+ description: "Match the tasks started after this date, in RFC 3339 format.",
40
+ helpValue: "<date>",
41
+ }),
42
+ "before-started-at": Flags.string({
43
+ description: "Match the tasks started before this date, in RFC 3339 format.",
44
+ helpValue: "<date>",
45
+ }),
46
+ "after-finished-at": Flags.string({
47
+ description: "Match the tasks finished after this date, in RFC 3339 format.",
48
+ helpValue: "<date>",
49
+ }),
50
+ "before-finished-at": Flags.string({
51
+ description: "Match the tasks finished before this date, in RFC 3339 format.",
52
+ helpValue: "<date>",
53
+ }),
54
+ };
55
+ export const taskFilterFlags = {
56
+ ids: Flags.string({
57
+ aliases: ["uids", "id"],
58
+ description: "Task uids to match, comma-separated. `*` matches every one.",
59
+ helpValue: "<uids>",
60
+ }),
61
+ "batch-ids": Flags.string({
62
+ aliases: ["batch-uids"],
63
+ description: "Match the tasks of these batch uids, comma-separated. `*` matches every one.",
64
+ helpValue: "<uids>",
65
+ }),
66
+ ...queueFilterFlags,
67
+ };
68
+ export const batchFilterFlags = {
69
+ ids: Flags.string({
70
+ aliases: ["batch-uids", "id"],
71
+ description: "Batch uids to match, comma-separated. `*` matches every one.",
72
+ helpValue: "<uids>",
73
+ }),
74
+ "task-ids": Flags.string({
75
+ aliases: ["uids"],
76
+ description: "Match the batches of these task uids, comma-separated. `*` matches every one.",
77
+ helpValue: "<uids>",
78
+ }),
79
+ ...queueFilterFlags,
80
+ };
81
+ /**
82
+ * The filters of the queue routes but the two identifier flags. The
83
+ * comma-separated list a flag carries is already the API's own GET syntax, so
84
+ * the text travels as it is: meilisearch-js types the identifier lists as
85
+ * arrays of numbers, which it joins back into the same request line, hence the
86
+ * cast of the two functions below.
87
+ */
88
+ function queueFilterQuery(flags) {
89
+ return {
90
+ indexUids: flags["index-ids"],
91
+ canceledBy: flags["canceled-by"],
92
+ types: flags.types,
93
+ statuses: flags.statuses,
94
+ afterEnqueuedAt: flags["after-enqueued-at"],
95
+ beforeEnqueuedAt: flags["before-enqueued-at"],
96
+ afterStartedAt: flags["after-started-at"],
97
+ beforeStartedAt: flags["before-started-at"],
98
+ afterFinishedAt: flags["after-finished-at"],
99
+ beforeFinishedAt: flags["before-finished-at"],
100
+ };
101
+ }
102
+ /** The filters of a task route, read from the flags the three commands share. */
103
+ export function taskFilterQuery(flags) {
104
+ return {
105
+ uids: flags.ids,
106
+ batchUids: flags["batch-ids"],
107
+ ...queueFilterQuery(flags),
108
+ };
109
+ }
110
+ /** The same filters for `GET /batches`, whose two identifier parameters are swapped. */
111
+ export function batchFilterQuery(flags) {
112
+ return {
113
+ batchUids: flags.ids,
114
+ uids: flags["task-ids"],
115
+ ...queueFilterQuery(flags),
116
+ };
117
+ }
118
+ //# sourceMappingURL=task-filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-filters.js","sourceRoot":"","sources":["../../src/lib/task-filters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { MeiliCliError } from "./errors.js";
2
+ export const DEFAULT_TIMEOUT = "10m";
3
+ const INITIAL_INTERVAL_MS = 100;
4
+ const MAX_INTERVAL_MS = 1000;
5
+ const FINAL_STATUSES = new Set(["succeeded", "failed", "canceled"]);
6
+ const FAILURE_STATUSES = new Set(["failed", "canceled"]);
7
+ export function isTaskFinal(task) {
8
+ return FINAL_STATUSES.has(task.status);
9
+ }
10
+ /** True when the task ended with `failed` or `canceled`: exit code 7. */
11
+ export function isTaskFailure(task) {
12
+ return FAILURE_STATUSES.has(task.status);
13
+ }
14
+ const DURATION = /^(\d+(?:\.\d+)?)(ms|s|m|h)$/;
15
+ const UNIT_MS = { ms: 1, s: 1000, m: 60_000, h: 3_600_000 };
16
+ /** Parses `500ms`, `30s`, `2m` into milliseconds. */
17
+ export function parseDuration(input) {
18
+ const match = input.trim().match(DURATION);
19
+ const unit = match?.[2];
20
+ if (match === null || match === undefined || unit === undefined) {
21
+ throw new MeiliCliError("cli_usage", `Invalid duration \`${input}\`. Use a number followed by ms, s, m or h, for example 500ms, 30s, 2m.`);
22
+ }
23
+ return Math.round(Number(match[1]) * UNIT_MS[unit]);
24
+ }
25
+ /** Renders milliseconds with the largest exact unit: 60000 is `1m`, 1500 is `1500ms`. */
26
+ export function formatDuration(ms) {
27
+ if (ms % 60_000 === 0)
28
+ return `${ms / 60_000}m`;
29
+ if (ms % 1000 === 0)
30
+ return `${ms / 1000}s`;
31
+ return `${ms}ms`;
32
+ }
33
+ /**
34
+ * Calls `poll` until `isFinal` holds, with a delay between calls that doubles
35
+ * from `initialIntervalMs` to `maxIntervalMs`. Throws `timeoutError()` when
36
+ * the deadline passes.
37
+ */
38
+ export async function pollUntil(poll, isFinal, timeoutError, options) {
39
+ const sleep = options.sleep ?? defaultSleep;
40
+ const deadline = Date.now() + options.timeoutMs;
41
+ let interval = options.initialIntervalMs ?? INITIAL_INTERVAL_MS;
42
+ for (;;) {
43
+ const current = await poll();
44
+ if (isFinal(current))
45
+ return current;
46
+ options.onProgress?.(current);
47
+ const remaining = deadline - Date.now();
48
+ if (remaining <= 0)
49
+ throw timeoutError();
50
+ await sleep(Math.min(interval, remaining));
51
+ interval = Math.min(interval * 2, options.maxIntervalMs ?? MAX_INTERVAL_MS);
52
+ }
53
+ }
54
+ /**
55
+ * Polls `GET /tasks/{uid}` until `succeeded`, `failed` or `canceled`. Throws
56
+ * `cli_timeout` when the deadline passes; the task keeps running on the server.
57
+ */
58
+ export function waitForTask(client, uid, options) {
59
+ return pollUntil(() => client.tasks.getTask(uid), isTaskFinal, () => new MeiliCliError("cli_timeout", `Task ${uid} did not reach a final status within ${formatDuration(options.timeoutMs)}. It keeps running on the server.`, { hint: `Run \`meili get tasks --ids ${uid}\` to check its status.` }), options);
60
+ }
61
+ function defaultSleep(ms) {
62
+ return new Promise((resolve) => setTimeout(resolve, ms));
63
+ }
64
+ //# sourceMappingURL=tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/lib/tasks.ts"],"names":[],"mappings":"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"}