@graphprotocol/graph-cli 0.88.0-alpha-20241031144836-2d9c5cd → 0.88.0-alpha-20241031145713-add2ae3

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/CHANGELOG.md CHANGED
@@ -1,26 +1,19 @@
1
1
  # @graphprotocol/graph-cli
2
2
 
3
- ## 0.88.0-alpha-20241031144836-2d9c5cd
3
+ ## 0.88.0-alpha-20241031145713-add2ae3
4
4
 
5
5
  ### Minor Changes
6
6
 
7
- - [#1680](https://github.com/graphprotocol/graph-tooling/pull/1680)
8
- [`69147a6`](https://github.com/graphprotocol/graph-tooling/commit/69147a64b35622d423936145fc0b2465a2afab17)
9
- Thanks [@saihaj](https://github.com/saihaj)! - Breaking changes to the CLI to prepare for the
10
- sunset of the hosted service.
11
-
12
- - `graph auth`
13
- - Removed `--product` flag
14
- - Removed `--studio` flag
15
- - Removed `node` argument
16
- - `graph deploy`
17
- - Removed `--product` flag
18
- - Removed `--studio` flag
19
- - Removed `--from-hosted-service` flag
20
- - `graph init`
21
- - Removed `--product` flag
22
- - Removed `--studio` flag
23
- - Removed `--allow-simple-name` flag
7
+ - [#1749](https://github.com/graphprotocol/graph-tooling/pull/1749)
8
+ [`bf43cbf`](https://github.com/graphprotocol/graph-tooling/commit/bf43cbf8283c53f0005d7cb9dfa681c92f3a6a91)
9
+ Thanks [@0237h](https://github.com/0237h)! - Add warning for available CLI updates
10
+
11
+ ### Patch Changes
12
+
13
+ - [#1751](https://github.com/graphprotocol/graph-tooling/pull/1751)
14
+ [`0ba99dc`](https://github.com/graphprotocol/graph-tooling/commit/0ba99dcd6783fb20779d4f5a6791fc155969939e)
15
+ Thanks [@0237h](https://github.com/0237h)! - Using `graph add` with `localhost` network now
16
+ prompts the user for input
24
17
 
25
18
  ## 0.87.0
26
19
 
package/README.md CHANGED
@@ -29,16 +29,12 @@ As of today, the command line interface supports the following commands:
29
29
  The Graph CLI takes a subgraph manifest (defaults to `subgraph.yaml`) with references to:
30
30
 
31
31
  - A GraphQL schema,
32
- - Smart contract ABIs,
33
- - Mappings written in AssemblyScript for traditional subgraphs,
34
- - Substreams package and triggers for substreams-based subgraphs
32
+ - Smart contract ABIs, and
33
+ - Mappings written in AssemblyScript.
35
34
 
36
35
  It compiles the mappings to WebAssembly, builds a ready-to-use version of the subgraph saved to IPFS
37
36
  or a local directory for debugging, and deploys the subgraph to a
38
- [Graph Node](https://github.com/graphprotocol/graph-node) instance or
39
- [Subgraph Studio](https://thegraph.com/studio/). Additionally it allows you to publish your subgraph
40
- to the decentralized network directly, making it available for indexing via
41
- [Graph Explorer](https://thegraph.com/explorer)
37
+ [Graph Node](https://github.com/graphprotocol/graph-node).
42
38
 
43
39
  ## Installation
44
40
 
@@ -77,15 +73,11 @@ Use one of the following commands depending on your distribution:
77
73
 
78
74
  The Graph CLI can be used with a local or self-hosted
79
75
  [Graph Node](https://github.com/graphprotocol/graph-node) or with the
80
- [Subgraph Studio](https://thegraph.com/studio/). To help you get going, there are
81
- [quick start guides](https://thegraph.com/docs/en/quick-start/) available for both.
82
-
83
- Additionally, you can use Graph CLI to
84
- [publish](https://thegraph.com/docs/en/quick-start/#publishing-from-the-cli) your subgraph to the
85
- decentralized network directly.
76
+ [Hosted Service](https://thegraph.com/explorer/). To help you get going, there are
77
+ [quick start guides](https://thegraph.com/docs/en/developer/quick-start/) available for both.
86
78
 
87
79
  If you are ready to dive into the details of building a subgraph from scratch, there is a
88
- [detailed walkthrough](https://thegraph.com/docs/en/developing/creating-a-subgraph/) for that as
80
+ [detailed walkthrough](https://thegraph.com/docs/en/developer/create-subgraph-hosted/) for that as
89
81
  well, along with API documentation for the
90
82
  [AssemblyScript API](https://thegraph.com/docs/en/developer/assemblyscript-api/).
91
83
 
@@ -3,8 +3,19 @@ import { URL } from 'url';
3
3
  export declare const SUBGRAPH_STUDIO_URL = "https://api.studio.thegraph.com/deploy/";
4
4
  export declare const validateNodeUrl: (node: string) => URL;
5
5
  export declare const normalizeNodeUrl: (node: string) => string;
6
- export declare function chooseNodeUrl({ node }: {
6
+ export declare function chooseNodeUrl({ product, studio, node, allowSimpleName, }: {
7
+ product: string | undefined;
8
+ studio: boolean | undefined;
7
9
  node?: string;
10
+ allowSimpleName?: boolean;
8
11
  }): {
9
- node: string;
12
+ node: string | undefined;
13
+ allowSimpleName: boolean | undefined;
10
14
  };
15
+ export declare function getHostedServiceSubgraphId({ subgraphName, }: {
16
+ subgraphName: string;
17
+ }): Promise<{
18
+ subgraph: string;
19
+ synced: boolean;
20
+ health: 'healthy' | 'unhealthy' | 'failed';
21
+ }>;
@@ -1,24 +1,74 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.chooseNodeUrl = exports.normalizeNodeUrl = exports.validateNodeUrl = exports.SUBGRAPH_STUDIO_URL = void 0;
3
+ exports.getHostedServiceSubgraphId = exports.chooseNodeUrl = exports.normalizeNodeUrl = exports.validateNodeUrl = exports.SUBGRAPH_STUDIO_URL = void 0;
4
4
  const url_1 = require("url");
5
5
  const gluegun_1 = require("gluegun");
6
+ const constants_1 = require("../constants");
6
7
  exports.SUBGRAPH_STUDIO_URL = 'https://api.studio.thegraph.com/deploy/';
8
+ const HOSTED_SERVICE_URL = 'https://api.thegraph.com/deploy/';
9
+ const HOSTED_SERVICE_INDEX_NODE_URL = 'https://api.thegraph.com/index-node/graphql';
7
10
  const validateNodeUrl = (node) => new url_1.URL(node);
8
11
  exports.validateNodeUrl = validateNodeUrl;
9
12
  const normalizeNodeUrl = (node) => new url_1.URL(node).toString();
10
13
  exports.normalizeNodeUrl = normalizeNodeUrl;
11
- function chooseNodeUrl({ node }) {
14
+ function chooseNodeUrl({ product, studio, node, allowSimpleName, }) {
12
15
  if (node) {
13
16
  try {
14
17
  (0, exports.validateNodeUrl)(node);
15
- return { node };
16
18
  }
17
19
  catch (e) {
18
20
  gluegun_1.print.error(`Graph node "${node}" is invalid: ${e.message}`);
19
21
  process.exit(1);
20
22
  }
21
23
  }
22
- return { node: exports.SUBGRAPH_STUDIO_URL };
24
+ else {
25
+ if (studio) {
26
+ product = 'subgraph-studio';
27
+ }
28
+ switch (product) {
29
+ case 'subgraph-studio':
30
+ node = exports.SUBGRAPH_STUDIO_URL;
31
+ break;
32
+ case 'hosted-service':
33
+ node = HOSTED_SERVICE_URL;
34
+ break;
35
+ }
36
+ }
37
+ if (node?.match(/studio/) || product === 'subgraph-studio') {
38
+ allowSimpleName = true;
39
+ }
40
+ return { node, allowSimpleName };
23
41
  }
24
42
  exports.chooseNodeUrl = chooseNodeUrl;
43
+ async function getHostedServiceSubgraphId({ subgraphName, }) {
44
+ const response = await fetch(HOSTED_SERVICE_INDEX_NODE_URL, {
45
+ method: 'POST',
46
+ body: JSON.stringify({
47
+ query: /* GraphQL */ `
48
+ query GraphCli_getSubgraphId($subgraphName: String!) {
49
+ indexingStatusForCurrentVersion(subgraphName: $subgraphName) {
50
+ subgraph
51
+ synced
52
+ health
53
+ }
54
+ }
55
+ `,
56
+ variables: {
57
+ subgraphName,
58
+ },
59
+ }),
60
+ headers: {
61
+ 'content-type': 'application/json',
62
+ ...constants_1.GRAPH_CLI_SHARED_HEADERS,
63
+ },
64
+ });
65
+ const { data, errors } = await response.json();
66
+ if (errors) {
67
+ throw new Error(errors[0].message);
68
+ }
69
+ if (!data.indexingStatusForCurrentVersion) {
70
+ throw new Error(`Subgraph "${subgraphName}" not found on the hosted service`);
71
+ }
72
+ return data.indexingStatusForCurrentVersion;
73
+ }
74
+ exports.getHostedServiceSubgraphId = getHostedServiceSubgraphId;
@@ -1 +1,4 @@
1
+ export declare const validateSubgraphName: (name: string, { allowSimpleName }: {
2
+ allowSimpleName?: boolean | undefined;
3
+ }) => string | undefined;
1
4
  export declare const getSubgraphBasename: (name: string) => string;
@@ -1,6 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSubgraphBasename = void 0;
3
+ exports.getSubgraphBasename = exports.validateSubgraphName = void 0;
4
+ const validateSubgraphName = (name, { allowSimpleName }) => {
5
+ if (allowSimpleName) {
6
+ return name;
7
+ }
8
+ if (name.split('/').length !== 2) {
9
+ throw new Error(`Subgraph name "${name}" needs to have the format "<PREFIX>/${name}".
10
+
11
+ When using the Hosted Service at https://thegraph.com, <PREFIX> is the
12
+ name of your GitHub user or organization.
13
+
14
+ You can bypass this check with --allow-simple-name.`);
15
+ }
16
+ };
17
+ exports.validateSubgraphName = validateSubgraphName;
4
18
  const getSubgraphBasename = (name) => {
5
19
  const segments = name.split('/', 2);
6
20
  return segments[segments.length - 1];
@@ -46,6 +46,9 @@ class AddCommand extends core_1.Command {
46
46
  const manifest = await subgraph_1.default.load(manifestPath, { protocol });
47
47
  const network = manifest.result.getIn(['dataSources', 0, 'network']);
48
48
  const result = manifest.result.asMutable();
49
+ const isLocalHost = network === 'localhost'; // This flag prevent Etherscan lookups in case the network selected is `localhost`
50
+ if (isLocalHost)
51
+ this.warn('`localhost` network detected, prompting user for inputs');
49
52
  let startBlock = startBlockFlag;
50
53
  let contractName = contractNameFlag;
51
54
  const entities = getEntities(manifest);
@@ -61,9 +64,46 @@ class AddCommand extends core_1.Command {
61
64
  ethabi = await (0, abi_1.loadAbiFromBlockScout)(abi_2.default, network, address);
62
65
  }
63
66
  else {
64
- ethabi = await (0, abi_1.loadAbiFromEtherscan)(abi_2.default, network, address);
67
+ try {
68
+ if (isLocalHost)
69
+ throw Error; // Triggers user prompting without waiting for Etherscan lookup to fail
70
+ ethabi = await (0, abi_1.loadAbiFromEtherscan)(abi_2.default, network, address);
71
+ }
72
+ catch (error) {
73
+ // we cannot ask user to do prompt in test environment
74
+ if (process.env.NODE_ENV !== 'test') {
75
+ const { abi: abiFromFile } = await gluegun_1.prompt.ask([
76
+ {
77
+ type: 'input',
78
+ name: 'abi',
79
+ message: 'ABI file (path)',
80
+ initial: ethabi,
81
+ validate: async (value) => {
82
+ try {
83
+ abi_2.default.load(contractName, value);
84
+ return true;
85
+ }
86
+ catch (e) {
87
+ this.error(e.message);
88
+ }
89
+ },
90
+ result: async (value) => {
91
+ try {
92
+ return abi_2.default.load(contractName, value);
93
+ }
94
+ catch (e) {
95
+ return e.message;
96
+ }
97
+ },
98
+ },
99
+ ]);
100
+ ethabi = abiFromFile;
101
+ }
102
+ }
65
103
  }
66
104
  try {
105
+ if (isLocalHost)
106
+ throw Error; // Triggers user prompting without waiting for Etherscan lookup to fail
67
107
  startBlock || (startBlock = Number(await (0, abi_1.loadStartBlockForContract)(network, address)).toString());
68
108
  }
69
109
  catch (error) {
@@ -86,6 +126,8 @@ class AddCommand extends core_1.Command {
86
126
  }
87
127
  }
88
128
  try {
129
+ if (isLocalHost)
130
+ throw Error; // Triggers user prompting without waiting for Etherscan lookup to fail
89
131
  contractName = await (0, abi_1.loadContractNameForAddress)(network, address);
90
132
  }
91
133
  catch (error) {
@@ -2,10 +2,13 @@ import { Command } from '@oclif/core';
2
2
  export default class AuthCommand extends Command {
3
3
  static description: string;
4
4
  static args: {
5
+ node: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
5
6
  'deploy-key': import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
6
7
  };
7
8
  static flags: {
8
9
  help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
10
+ product: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
+ studio: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
12
  };
10
13
  run(): Promise<void>;
11
14
  }
@@ -6,8 +6,23 @@ const auth_1 = require("../command-helpers/auth");
6
6
  const node_1 = require("../command-helpers/node");
7
7
  class AuthCommand extends core_1.Command {
8
8
  async run() {
9
- let { args: { 'deploy-key': deployKey }, } = await this.parse(AuthCommand);
10
- const { node } = (0, node_1.chooseNodeUrl)({});
9
+ const { args: { node: nodeOrDeployKey, 'deploy-key': deployKeyFlag }, flags: { product, studio }, } = await this.parse(AuthCommand);
10
+ // if user specifies --product or --studio then deployKey is the first parameter
11
+ let node;
12
+ let deployKey = deployKeyFlag;
13
+ if (product || studio) {
14
+ ({ node } = (0, node_1.chooseNodeUrl)({ product, studio, node }));
15
+ deployKey = nodeOrDeployKey;
16
+ }
17
+ else {
18
+ node = nodeOrDeployKey;
19
+ }
20
+ // eslint-disable-next-line -- prettier has problems with ||=
21
+ node =
22
+ node ||
23
+ (await core_1.ux.prompt('Which product to initialize?', {
24
+ required: true,
25
+ }));
11
26
  // eslint-disable-next-line -- prettier has problems with ||=
12
27
  deployKey =
13
28
  deployKey ||
@@ -18,6 +33,9 @@ class AuthCommand extends core_1.Command {
18
33
  if (deployKey.length > 200) {
19
34
  this.error('✖ Deploy key must not exceed 200 characters', { exit: 1 });
20
35
  }
36
+ if (product === 'hosted-service' || node?.match(/api.thegraph.com/)) {
37
+ this.error('✖ The hosted service is deprecated', { exit: 1 });
38
+ }
21
39
  try {
22
40
  await (0, auth_1.saveDeployKey)(node, deployKey);
23
41
  gluegun_1.print.success(`Deploy key set for ${node}`);
@@ -29,11 +47,26 @@ class AuthCommand extends core_1.Command {
29
47
  }
30
48
  AuthCommand.description = 'Sets the deploy key to use when deploying to a Graph node.';
31
49
  AuthCommand.args = {
50
+ node: core_1.Args.string(),
32
51
  'deploy-key': core_1.Args.string(),
33
52
  };
34
53
  AuthCommand.flags = {
35
54
  help: core_1.Flags.help({
36
55
  char: 'h',
37
56
  }),
57
+ product: core_1.Flags.string({
58
+ summary: 'Select a product for which to authenticate.',
59
+ options: ['subgraph-studio', 'hosted-service'],
60
+ deprecated: {
61
+ message: 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/',
62
+ },
63
+ }),
64
+ studio: core_1.Flags.boolean({
65
+ summary: 'Shortcut for "--product subgraph-studio".',
66
+ exclusive: ['product'],
67
+ deprecated: {
68
+ message: 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/',
69
+ },
70
+ }),
38
71
  };
39
72
  exports.default = AuthCommand;
@@ -7,6 +7,8 @@ export default class DeployCommand extends Command {
7
7
  };
8
8
  static flags: {
9
9
  help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
10
+ product: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
+ studio: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
12
  node: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
13
  'deploy-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
12
14
  'access-token': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
@@ -20,6 +22,7 @@ export default class DeployCommand extends Command {
20
22
  watch: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
23
  network: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
22
24
  'network-file': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
25
+ 'from-hosted-service': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
23
26
  };
24
27
  run(): Promise<void>;
25
28
  }
@@ -48,21 +48,149 @@ const headersFlag = core_1.Flags.custom({
48
48
  parse: val => JSON.parse(val),
49
49
  default: {},
50
50
  });
51
+ const productOptions = ['subgraph-studio', 'hosted-service'];
51
52
  const deployDebugger = (0, debug_1.default)('graph-cli:deploy');
52
53
  class DeployCommand extends core_1.Command {
53
54
  async run() {
54
- const { args: { 'subgraph-name': subgraphNameArg, 'subgraph-manifest': manifest }, flags: { 'deploy-key': deployKeyFlag, 'access-token': accessToken, 'version-label': versionLabelFlag, ipfs, headers, node: nodeFlag, 'output-dir': outputDir, 'skip-migrations': skipMigrations, watch, 'debug-fork': debugFork, network, 'network-file': networkFile, 'ipfs-hash': ipfsHash, }, } = await this.parse(DeployCommand);
55
+ const { args: { 'subgraph-name': subgraphNameArg, 'subgraph-manifest': manifest }, flags: { product: productFlag, studio, 'deploy-key': deployKeyFlag, 'access-token': accessToken, 'version-label': versionLabelFlag, ipfs, headers, node: nodeFlag, 'output-dir': outputDir, 'skip-migrations': skipMigrations, watch, 'debug-fork': debugFork, network, 'network-file': networkFile, 'ipfs-hash': ipfsHash, 'from-hosted-service': hostedServiceSubgraphName, }, } = await this.parse(DeployCommand);
56
+ if (hostedServiceSubgraphName) {
57
+ const { health, subgraph: subgraphIpfsHash } = await (0, node_1.getHostedServiceSubgraphId)({
58
+ subgraphName: hostedServiceSubgraphName,
59
+ });
60
+ const safeToDeployFailedSubgraph = health === 'failed'
61
+ ? await core_1.ux.confirm('This subgraph has failed indexing on hosted service. Do you wish to continue the deploy?')
62
+ : true;
63
+ if (!safeToDeployFailedSubgraph)
64
+ return;
65
+ const safeToDeployUnhealthySubgraph = health === 'unhealthy'
66
+ ? await core_1.ux.confirm('This subgraph is not healthy on hosted service. Do you wish to continue the deploy?')
67
+ : true;
68
+ if (!safeToDeployUnhealthySubgraph)
69
+ return;
70
+ const { node } = (0, node_1.chooseNodeUrl)({ studio: true, product: undefined });
71
+ // shouldn't happen, but we need to satisfy the compiler
72
+ if (!node) {
73
+ this.error('No node URL available');
74
+ }
75
+ const requestUrl = new url_1.URL(node);
76
+ const client = (0, jsonrpc_1.createJsonRpcClient)(requestUrl);
77
+ // Exit with an error code if the client couldn't be created
78
+ if (!client) {
79
+ this.error('Failed to create RPC client');
80
+ }
81
+ // Use the deploy key, if one is set
82
+ let deployKey = deployKeyFlag;
83
+ if (!deployKey && accessToken) {
84
+ deployKey = accessToken; // backwards compatibility
85
+ }
86
+ deployKey = await (0, auth_1.identifyDeployKey)(node, deployKey);
87
+ if (!deployKey) {
88
+ this.error('No deploy key available');
89
+ }
90
+ // @ts-expect-error options property seems to exist
91
+ client.options.headers = {
92
+ ...constants_1.GRAPH_CLI_SHARED_HEADERS,
93
+ Authorization: 'Bearer ' + deployKey,
94
+ };
95
+ const subgraphName = await core_1.ux.prompt('What is the name of the subgraph you want to deploy?', {
96
+ required: true,
97
+ });
98
+ // Ask for label if not on hosted service
99
+ const versionLabel = versionLabelFlag ||
100
+ (await core_1.ux.prompt('Which version label to use? (e.g. "v0.0.1")', {
101
+ required: true,
102
+ }));
103
+ const spinner = gluegun_1.print.spin(`Deploying to Graph node ${requestUrl}`);
104
+ client.request('subgraph_deploy', {
105
+ name: subgraphName,
106
+ ipfs_hash: subgraphIpfsHash,
107
+ version_label: versionLabel,
108
+ debug_fork: debugFork,
109
+ }, async (
110
+ // @ts-expect-error TODO: why are the arguments not typed?
111
+ requestError,
112
+ // @ts-expect-error TODO: why are the arguments not typed?
113
+ jsonRpcError,
114
+ // @ts-expect-error TODO: why are the arguments not typed?
115
+ res) => {
116
+ deployDebugger('requestError: %O', requestError);
117
+ deployDebugger('jsonRpcError: %O', jsonRpcError);
118
+ if (jsonRpcError) {
119
+ const message = jsonRpcError?.message || jsonRpcError?.code?.toString();
120
+ deployDebugger('message: %O', message);
121
+ let errorMessage = `Failed to deploy to Graph node ${requestUrl}: ${message}`;
122
+ // Provide helpful advice when the subgraph has not been created yet
123
+ if (message?.match(/subgraph name not found/)) {
124
+ errorMessage += `
125
+ Make sure to create the subgraph first by running the following command:
126
+ $ graph create --node ${node} ${subgraphName}`;
127
+ }
128
+ if (message?.match(/auth failure/)) {
129
+ errorMessage += '\nYou may need to authenticate first.';
130
+ }
131
+ spinner.fail(errorMessage);
132
+ process.exit(1);
133
+ }
134
+ else if (requestError) {
135
+ spinner.fail(`HTTP error deploying the subgraph ${requestError.code}`);
136
+ process.exit(1);
137
+ }
138
+ else {
139
+ spinner.stop();
140
+ const base = requestUrl.protocol + '//' + requestUrl.hostname;
141
+ let playground = res.playground;
142
+ let queries = res.queries;
143
+ // Add a base URL if graph-node did not return the full URL
144
+ if (playground.charAt(0) === ':') {
145
+ playground = base + playground;
146
+ }
147
+ if (queries.charAt(0) === ':') {
148
+ queries = base + queries;
149
+ }
150
+ gluegun_1.print.success(`Deployed to ${playground}`);
151
+ gluegun_1.print.info('\nSubgraph endpoints:');
152
+ gluegun_1.print.info(`Queries (HTTP): ${queries}`);
153
+ gluegun_1.print.info(``);
154
+ process.exit(0);
155
+ }
156
+ });
157
+ return;
158
+ }
55
159
  const subgraphName = subgraphNameArg ||
56
160
  (await core_1.ux.prompt('What is the subgraph name?', {
57
161
  required: true,
58
162
  }));
163
+ // We are given a node URL, so we prioritize that over the product flag
164
+ const product = nodeFlag
165
+ ? productFlag
166
+ : studio
167
+ ? 'subgraph-studio'
168
+ : productFlag ||
169
+ (await gluegun_1.prompt
170
+ .ask([
171
+ {
172
+ name: 'product',
173
+ message: 'Which product to deploy for?',
174
+ required: true,
175
+ type: 'select',
176
+ choices: productOptions,
177
+ },
178
+ ])
179
+ .then(({ product }) => product));
180
+ if (product === 'hosted-service') {
181
+ this.error('✖ The hosted service is deprecated', { exit: 1 });
182
+ }
59
183
  const { node } = (0, node_1.chooseNodeUrl)({
184
+ product,
185
+ studio,
60
186
  node: nodeFlag,
61
187
  });
62
188
  if (!node) {
63
189
  // shouldn't happen, but we do the check to satisfy TS
64
190
  this.error('No Graph node provided');
65
191
  }
192
+ const isStudio = node.match(/studio/);
193
+ const isHostedService = node.match(/thegraph.com/) && !isStudio;
66
194
  const requestUrl = new url_1.URL(node);
67
195
  const client = (0, jsonrpc_1.createJsonRpcClient)(requestUrl);
68
196
  // Exit with an error code if the client couldn't be created
@@ -85,9 +213,11 @@ class DeployCommand extends core_1.Command {
85
213
  }
86
214
  // Ask for label if not on hosted service
87
215
  let versionLabel = versionLabelFlag;
88
- versionLabel || (versionLabel = await core_1.ux.prompt('Which version label to use? (e.g. "v0.0.1")', {
89
- required: true,
90
- }));
216
+ if (!versionLabel && !isHostedService) {
217
+ versionLabel = await core_1.ux.prompt('Which version label to use? (e.g. "v0.0.1")', {
218
+ required: true,
219
+ });
220
+ }
91
221
  const deploySubgraph = async (ipfsHash) => {
92
222
  const spinner = gluegun_1.print.spin(`Deploying to Graph node ${requestUrl}`);
93
223
  client.request('subgraph_deploy', {
@@ -108,6 +238,18 @@ class DeployCommand extends core_1.Command {
108
238
  const message = jsonRpcError?.message || jsonRpcError?.code?.toString();
109
239
  deployDebugger('message: %O', message);
110
240
  let errorMessage = `Failed to deploy to Graph node ${requestUrl}: ${message}`;
241
+ // Provide helpful advice when the subgraph has not been created yet
242
+ if (message?.match(/subgraph name not found/)) {
243
+ if (isHostedService) {
244
+ errorMessage +=
245
+ '\nYou may need to create it at https://thegraph.com/explorer/dashboard.';
246
+ }
247
+ else {
248
+ errorMessage += `
249
+ Make sure to create the subgraph first by running the following command:
250
+ $ graph create --node ${node} ${subgraphName}`;
251
+ }
252
+ }
111
253
  if (message?.match(/auth failure/)) {
112
254
  errorMessage += '\nYou may need to authenticate first.';
113
255
  }
@@ -130,7 +272,12 @@ class DeployCommand extends core_1.Command {
130
272
  if (queries.charAt(0) === ':') {
131
273
  queries = base + queries;
132
274
  }
133
- gluegun_1.print.success(`Deployed to ${playground}`);
275
+ if (isHostedService) {
276
+ gluegun_1.print.success(`Deployed to https://thegraph.com/explorer/subgraph/${subgraphName}`);
277
+ }
278
+ else {
279
+ gluegun_1.print.success(`Deployed to ${playground}`);
280
+ }
134
281
  gluegun_1.print.info('\nSubgraph endpoints:');
135
282
  gluegun_1.print.info(`Queries (HTTP): ${queries}`);
136
283
  gluegun_1.print.info(``);
@@ -224,6 +371,14 @@ DeployCommand.flags = {
224
371
  help: core_1.Flags.help({
225
372
  char: 'h',
226
373
  }),
374
+ product: core_1.Flags.string({
375
+ summary: 'Select a product for which to authenticate.',
376
+ options: productOptions,
377
+ }),
378
+ studio: core_1.Flags.boolean({
379
+ summary: 'Shortcut for "--product subgraph-studio".',
380
+ exclusive: ['product'],
381
+ }),
227
382
  node: core_1.Flags.string({
228
383
  summary: 'Graph node for which to initialize.',
229
384
  char: 'g',
@@ -271,9 +426,13 @@ DeployCommand.flags = {
271
426
  network: core_1.Flags.string({
272
427
  summary: 'Network configuration to use from the networks config file.',
273
428
  }),
429
+ // TODO: should be networksFile (with an "s"), or?
274
430
  'network-file': core_1.Flags.file({
275
431
  summary: 'Networks config file path.',
276
432
  default: 'networks.json',
277
433
  }),
434
+ 'from-hosted-service': core_1.Flags.string({
435
+ summary: 'Hosted service Subgraph Name to deploy to studio.',
436
+ }),
278
437
  };
279
438
  exports.default = DeployCommand;
@@ -8,7 +8,10 @@ export default class InitCommand extends Command {
8
8
  static flags: {
9
9
  help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
10
10
  protocol: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
+ product: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
12
+ studio: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
13
  node: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
14
+ 'allow-simple-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
15
  'from-contract': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
13
16
  'from-example': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
14
17
  'contract-name': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
@@ -88,10 +88,17 @@ const DEFAULT_EXAMPLE_SUBGRAPH = 'ethereum-gravatar';
88
88
  class InitCommand extends core_1.Command {
89
89
  async run() {
90
90
  const { args: { subgraphName, directory }, flags, } = await this.parse(InitCommand);
91
- const { protocol, node: nodeFlag, 'from-contract': fromContract, 'contract-name': contractName, 'from-example': fromExample, 'index-events': indexEvents, 'skip-install': skipInstall, 'skip-git': skipGit, network, abi: abiPath, 'start-block': startBlock, spkg: spkgPath, } = flags;
91
+ const { protocol, product, studio, node: nodeFlag, 'allow-simple-name': allowSimpleNameFlag, 'from-contract': fromContract, 'contract-name': contractName, 'from-example': fromExample, 'index-events': indexEvents, 'skip-install': skipInstall, 'skip-git': skipGit, network, abi: abiPath, 'start-block': startBlock, spkg: spkgPath, } = flags;
92
92
  initDebugger('Flags: %O', flags);
93
- let { node } = (0, node_1.chooseNodeUrl)({
93
+ if (product === 'hosted-service') {
94
+ this.error('✖ The hosted service is deprecated', { exit: 1 });
95
+ }
96
+ let { node, allowSimpleName } = (0, node_1.chooseNodeUrl)({
97
+ product,
98
+ // if we are loading example, we want to ensure we are using studio
99
+ studio: studio || fromExample !== undefined,
94
100
  node: nodeFlag,
101
+ allowSimpleName: allowSimpleNameFlag,
95
102
  });
96
103
  if (fromContract && fromExample) {
97
104
  this.error('Only one of "--from-example" and "--from-contract" can be used at a time.', {
@@ -124,6 +131,7 @@ class InitCommand extends core_1.Command {
124
131
  if (fromExample && subgraphName && directory) {
125
132
  await initSubgraphFromExample.bind(this)({
126
133
  fromExample,
134
+ allowSimpleName,
127
135
  directory,
128
136
  subgraphName,
129
137
  skipInstall,
@@ -169,6 +177,7 @@ class InitCommand extends core_1.Command {
169
177
  await initSubgraphFromContract.bind(this)({
170
178
  protocolInstance,
171
179
  abi,
180
+ allowSimpleName,
172
181
  directory,
173
182
  contract: fromContract,
174
183
  indexEvents,
@@ -186,6 +195,7 @@ class InitCommand extends core_1.Command {
186
195
  }
187
196
  if (fromExample) {
188
197
  const answers = await processFromExampleInitForm.bind(this)({
198
+ allowSimpleName,
189
199
  subgraphName,
190
200
  directory,
191
201
  });
@@ -194,6 +204,7 @@ class InitCommand extends core_1.Command {
194
204
  return;
195
205
  }
196
206
  await initSubgraphFromExample.bind(this)({
207
+ allowSimpleName,
197
208
  fromExample,
198
209
  subgraphName: answers.subgraphName,
199
210
  directory: answers.directory,
@@ -205,8 +216,12 @@ class InitCommand extends core_1.Command {
205
216
  // Otherwise, take the user through the interactive form
206
217
  const answers = await processInitForm.bind(this)({
207
218
  protocol: protocol,
219
+ product,
220
+ studio,
221
+ node,
208
222
  abi,
209
223
  abiPath,
224
+ allowSimpleName,
210
225
  directory,
211
226
  contract: fromContract,
212
227
  indexEvents,
@@ -221,11 +236,15 @@ class InitCommand extends core_1.Command {
221
236
  this.exit(1);
222
237
  return;
223
238
  }
224
- ({ node } = (0, node_1.chooseNodeUrl)({
239
+ ({ node, allowSimpleName } = (0, node_1.chooseNodeUrl)({
240
+ product: answers.product,
241
+ studio: answers.studio,
225
242
  node,
243
+ allowSimpleName,
226
244
  }));
227
245
  await initSubgraphFromContract.bind(this)({
228
246
  protocolInstance: answers.protocolInstance,
247
+ allowSimpleName,
229
248
  subgraphName: answers.subgraphName,
230
249
  directory: answers.directory,
231
250
  abi: answers.abi,
@@ -256,10 +275,31 @@ InitCommand.flags = {
256
275
  protocol: core_1.Flags.string({
257
276
  options: protocolChoices,
258
277
  }),
278
+ product: core_1.Flags.string({
279
+ summary: 'Selects the product for which to initialize.',
280
+ options: ['subgraph-studio', 'hosted-service'],
281
+ deprecated: {
282
+ message: 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/',
283
+ },
284
+ }),
285
+ studio: core_1.Flags.boolean({
286
+ summary: 'Shortcut for "--product subgraph-studio".',
287
+ exclusive: ['product'],
288
+ deprecated: {
289
+ message: 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/',
290
+ },
291
+ }),
259
292
  node: core_1.Flags.string({
260
293
  summary: 'Graph node for which to initialize.',
261
294
  char: 'g',
262
295
  }),
296
+ 'allow-simple-name': core_1.Flags.boolean({
297
+ description: 'Use a subgraph name without a prefix.',
298
+ default: false,
299
+ deprecated: {
300
+ message: 'In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/',
301
+ },
302
+ }),
263
303
  'from-contract': core_1.Flags.string({
264
304
  description: 'Creates a scaffold based on an existing contract.',
265
305
  exclusive: ['from-example'],
@@ -314,14 +354,31 @@ InitCommand.flags = {
314
354
  }),
315
355
  };
316
356
  exports.default = InitCommand;
317
- async function processFromExampleInitForm({ directory: initDirectory, subgraphName: initSubgraphName, }) {
357
+ async function processFromExampleInitForm({ directory: initDirectory, subgraphName: initSubgraphName, allowSimpleName: initAllowSimpleName, }) {
318
358
  try {
319
359
  const { subgraphName } = await gluegun_1.prompt.ask([
320
360
  {
321
361
  type: 'input',
322
362
  name: 'subgraphName',
363
+ // TODO: is defaulting to studio ok?
323
364
  message: () => 'Subgraph slug',
324
365
  initial: initSubgraphName,
366
+ validate: name => {
367
+ try {
368
+ (0, subgraph_1.validateSubgraphName)(name, {
369
+ allowSimpleName: initAllowSimpleName,
370
+ });
371
+ return true;
372
+ }
373
+ catch (e) {
374
+ return `${e.message}
375
+
376
+ Examples:
377
+
378
+ $ graph init ${os_1.default.userInfo().username}/${name}
379
+ $ graph init ${name} --allow-simple-name`;
380
+ }
381
+ },
325
382
  },
326
383
  ]);
327
384
  const { directory } = await gluegun_1.prompt.ask([
@@ -363,7 +420,7 @@ async function retryWithPrompt(func) {
363
420
  }
364
421
  return undefined;
365
422
  }
366
- async function processInitForm({ protocol: initProtocol, abi: initAbi, abiPath: initAbiPath, directory: initDirectory, contract: initContract, indexEvents: initIndexEvents, fromExample: initFromExample, network: initNetwork, subgraphName: initSubgraphName, contractName: initContractName, startBlock: initStartBlock, spkgPath: initSpkgPath, }) {
423
+ async function processInitForm({ protocol: initProtocol, product: initProduct, studio: initStudio, node: initNode, abi: initAbi, abiPath: initAbiPath, directory: initDirectory, contract: initContract, indexEvents: initIndexEvents, fromExample: initFromExample, network: initNetwork, subgraphName: initSubgraphName, contractName: initContractName, startBlock: initStartBlock, allowSimpleName: initAllowSimpleName, spkgPath: initSpkgPath, }) {
367
424
  let abiFromEtherscan = undefined;
368
425
  try {
369
426
  const { protocol } = await gluegun_1.prompt.ask({
@@ -384,12 +441,60 @@ async function processInitForm({ protocol: initProtocol, abi: initAbi, abiPath:
384
441
  const protocolInstance = new protocols_1.default(protocol);
385
442
  const isSubstreams = protocol === 'substreams';
386
443
  initDebugger.extend('processInitForm')('isSubstreams: %O', isSubstreams);
444
+ const { product } = await gluegun_1.prompt.ask([
445
+ {
446
+ type: 'select',
447
+ name: 'product',
448
+ message: 'Product for which to initialize',
449
+ choices: ['subgraph-studio', 'hosted-service'],
450
+ skip: protocol === 'arweave' ||
451
+ protocol === 'cosmos' ||
452
+ protocol === 'near' ||
453
+ initProduct === 'subgraph-studio' ||
454
+ initProduct === 'hosted-service' ||
455
+ initStudio !== undefined ||
456
+ initNode !== undefined,
457
+ result: value => {
458
+ if (initProduct)
459
+ return initProduct;
460
+ if (initStudio)
461
+ return 'subgraph-studio';
462
+ // For now we only support NEAR subgraphs in the Hosted Service
463
+ if (protocol === 'near') {
464
+ return 'hosted-service';
465
+ }
466
+ if (value == 'subgraph-studio') {
467
+ initAllowSimpleName = true;
468
+ }
469
+ return value;
470
+ },
471
+ },
472
+ ]);
473
+ if (product == 'hosted-service') {
474
+ this.error('✖ The hosted service is deprecated', { exit: 1 });
475
+ }
387
476
  const { subgraphName } = await gluegun_1.prompt.ask([
388
477
  {
389
478
  type: 'input',
390
479
  name: 'subgraphName',
391
- message: 'Subgraph slug',
480
+ message: () => (product == 'subgraph-studio' ? 'Subgraph slug' : 'Subgraph name'),
392
481
  initial: initSubgraphName,
482
+ validate: name => {
483
+ try {
484
+ (0, subgraph_1.validateSubgraphName)(name, {
485
+ allowSimpleName: initAllowSimpleName,
486
+ });
487
+ return true;
488
+ }
489
+ catch (e) {
490
+ return `${e.message}
491
+
492
+ Examples:
493
+
494
+ $ graph init ${os_1.default.userInfo().username}/${name}
495
+ $ graph init ${name} --allow-simple-name`;
496
+ }
497
+ },
393
498
  },
394
499
  ]);
395
500
  const { directory } = await gluegun_1.prompt.ask([
@@ -403,7 +508,7 @@ async function processInitForm({ protocol: initProtocol, abi: initAbi, abiPath:
403
508
  : true,
404
509
  },
405
510
  ]);
406
- let choices = (await AVAILABLE_NETWORKS())?.['studio'];
511
+ let choices = (await AVAILABLE_NETWORKS())?.[product === 'subgraph-studio' ? 'studio' : 'hostedService'];
407
512
  if (!choices) {
408
513
  this.error('Unable to fetch available networks from API. Please report this issue. As a workaround you can pass `--network` flag from the available networks: https://thegraph.com/docs/en/developing/supported-networks', { exit: 1 });
409
514
  }
@@ -591,8 +696,10 @@ async function processInitForm({ protocol: initProtocol, abi: initAbi, abiPath:
591
696
  protocolInstance,
592
697
  subgraphName,
593
698
  directory,
699
+ studio: product === 'subgraph-studio',
594
700
  startBlock,
595
701
  fromExample: !!initFromExample,
702
+ product,
596
703
  network,
597
704
  contractName,
598
705
  contract,
@@ -619,6 +726,21 @@ const loadAbiFromFile = (ABI, filename) => {
619
726
  return ABI.load('Contract', filename);
620
727
  }
621
728
  };
729
+ function revalidateSubgraphName(subgraphName, { allowSimpleName }) {
730
+ // Fail if the subgraph name is invalid
731
+ try {
732
+ (0, subgraph_1.validateSubgraphName)(subgraphName, { allowSimpleName });
733
+ return true;
734
+ }
735
+ catch (e) {
736
+ this.error(`${e.message}
737
+
738
+ Examples:
739
+
740
+ $ graph init ${os_1.default.userInfo().username}/${subgraphName}
741
+ $ graph init ${subgraphName} --allow-simple-name`);
742
+ }
743
+ }
622
744
  // Inspired from: https://github.com/graphprotocol/graph-tooling/issues/1450#issuecomment-1713992618
623
745
  async function isInRepo() {
624
746
  try {
@@ -684,7 +806,12 @@ Subgraph ${subgraphName} created in ${relativeDir}
684
806
 
685
807
  Make sure to visit the documentation on https://thegraph.com/docs/ for further information.`);
686
808
  }
687
- async function initSubgraphFromExample({ fromExample, subgraphName, directory, skipInstall, skipGit, }, { commands, }) {
809
+ async function initSubgraphFromExample({ fromExample, allowSimpleName, subgraphName, directory, skipInstall, skipGit, }, { commands, }) {
810
+ // Fail if the subgraph name is invalid
811
+ if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
812
+ process.exitCode = 1;
813
+ return;
814
+ }
688
815
  // Fail if the output directory already exists
689
816
  if (gluegun_1.filesystem.exists(directory)) {
690
817
  this.error(`Directory or file "${directory}" already exists`, { exit: 1 });
@@ -777,8 +904,13 @@ async function initSubgraphFromExample({ fromExample, subgraphName, directory, s
777
904
  }
778
905
  printNextSteps.bind(this)({ subgraphName, directory }, { commands });
779
906
  }
780
- async function initSubgraphFromContract({ protocolInstance, subgraphName, directory, abi, network, contract, indexEvents, contractName, node, startBlock, spkgPath, skipInstall, skipGit, }, { commands, addContract, }) {
907
+ async function initSubgraphFromContract({ protocolInstance, allowSimpleName, subgraphName, directory, abi, network, contract, indexEvents, contractName, node, startBlock, spkgPath, skipInstall, skipGit, }, { commands, addContract, }) {
781
908
  const isSubstreams = protocolInstance.name === 'substreams';
909
+ // Fail if the subgraph name is invalid
910
+ if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
911
+ this.exit(1);
912
+ return;
913
+ }
782
914
  // Fail if the output directory already exists
783
915
  if (gluegun_1.filesystem.exists(directory)) {
784
916
  this.error(`Directory or file "${directory}" already exists`, { exit: 1 });
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.88.0-alpha-20241031144836-2d9c5cd",
2
+ "version": "0.88.0-alpha-20241031145713-add2ae3",
3
3
  "commands": {
4
4
  "add": {
5
5
  "id": "add",
@@ -77,9 +77,37 @@
77
77
  "char": "h",
78
78
  "description": "Show CLI help.",
79
79
  "allowNo": false
80
+ },
81
+ "product": {
82
+ "name": "product",
83
+ "type": "option",
84
+ "summary": "Select a product for which to authenticate.",
85
+ "multiple": false,
86
+ "options": [
87
+ "subgraph-studio",
88
+ "hosted-service"
89
+ ],
90
+ "deprecated": {
91
+ "message": "In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/"
92
+ }
93
+ },
94
+ "studio": {
95
+ "name": "studio",
96
+ "type": "boolean",
97
+ "summary": "Shortcut for \"--product subgraph-studio\".",
98
+ "allowNo": false,
99
+ "exclusive": [
100
+ "product"
101
+ ],
102
+ "deprecated": {
103
+ "message": "In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/"
104
+ }
80
105
  }
81
106
  },
82
107
  "args": {
108
+ "node": {
109
+ "name": "node"
110
+ },
83
111
  "deploy-key": {
84
112
  "name": "deploy-key"
85
113
  }
@@ -317,6 +345,25 @@
317
345
  "description": "Show CLI help.",
318
346
  "allowNo": false
319
347
  },
348
+ "product": {
349
+ "name": "product",
350
+ "type": "option",
351
+ "summary": "Select a product for which to authenticate.",
352
+ "multiple": false,
353
+ "options": [
354
+ "subgraph-studio",
355
+ "hosted-service"
356
+ ]
357
+ },
358
+ "studio": {
359
+ "name": "studio",
360
+ "type": "boolean",
361
+ "summary": "Shortcut for \"--product subgraph-studio\".",
362
+ "allowNo": false,
363
+ "exclusive": [
364
+ "product"
365
+ ]
366
+ },
320
367
  "node": {
321
368
  "name": "node",
322
369
  "type": "option",
@@ -416,6 +463,12 @@
416
463
  "summary": "Networks config file path.",
417
464
  "multiple": false,
418
465
  "default": "networks.json"
466
+ },
467
+ "from-hosted-service": {
468
+ "name": "from-hosted-service",
469
+ "type": "option",
470
+ "summary": "Hosted service Subgraph Name to deploy to studio.",
471
+ "multiple": false
419
472
  }
420
473
  },
421
474
  "args": {
@@ -456,6 +509,31 @@
456
509
  "substreams"
457
510
  ]
458
511
  },
512
+ "product": {
513
+ "name": "product",
514
+ "type": "option",
515
+ "summary": "Selects the product for which to initialize.",
516
+ "multiple": false,
517
+ "options": [
518
+ "subgraph-studio",
519
+ "hosted-service"
520
+ ],
521
+ "deprecated": {
522
+ "message": "In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/"
523
+ }
524
+ },
525
+ "studio": {
526
+ "name": "studio",
527
+ "type": "boolean",
528
+ "summary": "Shortcut for \"--product subgraph-studio\".",
529
+ "allowNo": false,
530
+ "exclusive": [
531
+ "product"
532
+ ],
533
+ "deprecated": {
534
+ "message": "In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/"
535
+ }
536
+ },
459
537
  "node": {
460
538
  "name": "node",
461
539
  "type": "option",
@@ -463,6 +541,15 @@
463
541
  "summary": "Graph node for which to initialize.",
464
542
  "multiple": false
465
543
  },
544
+ "allow-simple-name": {
545
+ "name": "allow-simple-name",
546
+ "type": "boolean",
547
+ "description": "Use a subgraph name without a prefix.",
548
+ "allowNo": false,
549
+ "deprecated": {
550
+ "message": "In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/"
551
+ }
552
+ },
466
553
  "from-contract": {
467
554
  "name": "from-contract",
468
555
  "type": "option",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphprotocol/graph-cli",
3
- "version": "0.88.0-alpha-20241031144836-2d9c5cd",
3
+ "version": "0.88.0-alpha-20241031145713-add2ae3",
4
4
  "description": "CLI for building for and deploying to The Graph",
5
5
  "license": "(Apache-2.0 OR MIT)",
6
6
  "engines": {
@@ -23,6 +23,7 @@
23
23
  "@oclif/core": "2.8.6",
24
24
  "@oclif/plugin-autocomplete": "^2.3.6",
25
25
  "@oclif/plugin-not-found": "^2.4.0",
26
+ "@oclif/plugin-warn-if-update-available": "^3.1.20",
26
27
  "@whatwg-node/fetch": "^0.8.4",
27
28
  "assemblyscript": "0.19.23",
28
29
  "binary-install-raw": "0.0.13",
@@ -76,8 +77,13 @@
76
77
  ],
77
78
  "plugins": [
78
79
  "@oclif/plugin-not-found",
79
- "@oclif/plugin-autocomplete"
80
- ]
80
+ "@oclif/plugin-autocomplete",
81
+ "@oclif/plugin-warn-if-update-available"
82
+ ],
83
+ "warn-if-update-available": {
84
+ "timeoutInDays": 1,
85
+ "message": "<%= config.name %> update available from <%= chalk.yellowBright(config.version) %> to <%= chalk.greenBright(latest) %>."
86
+ }
81
87
  },
82
88
  "scripts": {
83
89
  "build": "tsc -b tsconfig.build.json && oclif manifest && oclif readme && copyfiles -u 1 src/**/*.graphql dist/",